TGPBitmapでファイルにセーブしても保存されないのは何故?

解決


onodat  2025-04-11 18:23:06  No: 151928  IP: [192.*.*.*]

環境は「Windows7 32bit delphi 10.1 Berlin」です。
質問は、テキストデータと画像を保存したファイルから画像を読み出すとき
1.TFileStreamにファイルから読み込む
2.TFileStreamの画像部分の位置からをTMemoryStreamに読み込む
        MemStream.Position := 0;
        MemStream.CopyFrom(FileStream, サイズ)
3.GPBitmap := TGPBitmap.Create(TStreamAdapter.Create(MemStream));
  GPBitmap.Save(fName, ImageBMPClsid);でfNameが作成される

Windows7 32bit での実行では一時ファイル(fName)問題無し。
Windows10 64bit での実行では一時ファイル(fName)が作られない。エラーも無い。

当然ではあるが、Windows10 64bitでのファイルはコピーできる。
しかし、GPBitmap.Save(fName, ImageBMPClsid);ではfNameが作成されない。まるで空振り状態。

64bitでは、画像の仮ファイルを作成するには何か関門があるのでしょうか?
対応方法をご教示願います。
以上

編集    削除
AAAAA  2025-04-12 06:59:11  No: 151929  IP: [192.*.*.*]


bmp :TGPBitmapをjpg(またはbmp)ファイルに保存するには。
https://www.petitmonte.com/bbs/answers?question_id=7632
で出てくる
http://blog.livedoor.jp/junki560/archives/22895515.html
これは 64bit でも動く

MemStream を保存して画像になっているか確認
GPBitmap.Save(fName, ImageBMPClsid); で想定外のところに作られているか?

ぐらいかね


編集    削除
onodat  2025-04-12 10:25:43  No: 151930  IP: [192.*.*.*]

有難うございます。
確かに、例示の通り画像ファイルを直に読み込めば、問題無く実行できます。問題は、TFileStream内の画像データを読み込みです。
試しに、TWICImageを使っても、32bitでは一時ファイルが作られ問題なし、64bitは「画像が不正です」との警告が出る(当然一時ファイルは出来ない)。
以上

編集    削除
AAAAA  2025-04-12 20:41:17  No: 151931  IP: [192.*.*.*]

var
    GPBitmap: TGPBitmap;
    MemStream: TMemoryStream;
begin
    MemStream := TMemoryStream.Create;
    MemStream.LoadFromFile('A.BMP');

    GPBitmap := TGPBitmap.Create(TStreamAdapter.Create(MemStream));
  GPBitmap.Save('B.BMP', ImageBMPClsid);

で 64bit でも保存される

64bitは「画像が不正です」との警告が出る
ということは

2.TFileStreamの画像部分の位置からをTMemoryStreamに読み込む

ここで 問題がおきてるんじゃなのか?

編集    削除
onodat  2025-04-13 11:59:20  No: 151932  IP: [192.*.*.*]

有難うございます。

windows10に限り、

 画像ファイルの直読みで、jpgはOK、tifは何故だか読み込めない(多分ファイル名が文字化けしているせいか疑っている→これから調査)。
GPBitmap := TGPBitmap.Create(画像ファイル名);

MemStreamからの読み込みは全てダメ。
GPBitmap := TGPBitmap.Create(TStreamAdapter.Create(MemStream));

bmpの一時ファイル名に書き出し。
GPBitmap.Save(一時ファイル名, ImageBMPClsid);
windows7では「一時ファイル」が出来るのに、windows10では何故出来ない?

編集    削除
AAAAA  2025-04-13 12:20:24  No: 151933  IP: [192.*.*.*]

>windows7では「一時ファイル」が出来るのに、windows10では何故出来ない?

>試しに、TWICImageを使っても、32bitでは一時ファイルが作られ問題なし、64bitは「画像が不正です」との警告が出る(当然一時ファイルは出来>
ない)。

何故って画像が不正だからでしょ?

問題点は
テキストデータと画像を保存したファイルから画像を読み出すとき windows7 (32bit) と Windows10 (64bit)が違うのはなぜ?
だと思うんだけど

テキストデータと画像を保存したファイルから画像を読み出すときの
この元ファイル WIndows7 と Windows10 同じ?

Windws7 側と Windows10 側でファイルが異なっていて windows10 側が壊れてる だと思うんだけど

取り出すときに 32bit と 64bit で動作変わる事とか やってなければ

編集    削除
Mr.XRAY  2025-04-14 12:43:39  No: 151934  IP: [192.*.*.*]

テストしてみました.
テストに使用したコードです.

//=============================================================================
//  保存
//
//  動作確認は Windows 10 ビルド 19045 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
//  64 bit ( VCL 64 ) は未確認.Windows 7 での動作も未確認
//
//  動作確認には,TGPBitmap が認識可能な画像ファイルを用意する必要あり
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFileStream : TFileStream;
  LByteStream : TBytesStream;
  LpBuffer    : array [0..255] of Char;
  LGPBitmap   : TGPBitmap;
  LText       : string;
begin
  DeleteFile('000.dat');
  DeleteFile('503_Save.bmp');


  LText := 'テスト用テキスト' + sLineBreak + 'ビットマップの保存読み出し';

  LFileStream := TFileStream.Create('000.dat', fmCreate);
  LGPBitmap := TGPBitmap.Create('503.bmp');
  LByteStream := TBytesStream.Create;
  try
    FillChar(LpBuffer, SizeOf(LpBuffer), #0);
      StrPCopy(@LpBuffer[0], LText);
      LFileStream.Write(@LpBuffer[0], SizeOf(LpBuffer));

      LGPBitmap.Save(TStreamAdapter.Create(LByteStream), ImageBMPClsid);
      LFileStream.Write(LByteStream.Bytes[0], LByteStream.Size);
  finally
    FreeAndNil(LFileStream);
    FreeAndNil(LByteStream);
    FreeAndNil(LGPBitmap);
  end;
end;

//=============================================================================
///  読み出し
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LFileStream : TFileStream;
  LByteStream : TBytesStream;
  LpBuffer    : array [0..255] of Char;
  LGPBitmap   : TGPBitmap;
  LText       : string;
  LReadSize   : Integer;
begin
  DeleteFile('503_Save.bmp');

  LFileStream := TFileStream.Create('000.dat', fmOpenRead);
  LByteStream := TBytesStream.Create;
  try
     FillChar(LpBuffer, SizeOf(LpBuffer), #0);
      LFileStream.Read(LpBuffer[0], SizeOf(LpBuffer));
      LText := Trim(LpBuffer);

      LReadSize := LFileStream.Size - LFileStream.Position;
      LByteStream.Size := LReadSize;
      LFileStream.Read(LByteStream.Bytes[0], LReadSize);

      LGPBitmap := TGPBitmap.Create(TStreamAdapter.Create(LByteStream));
      LGPBitmap.Save('503_Save.bmp', ImageBMPClsid);
  finally
    FreeAndNil(LFileStream);
    FreeAndNil(LByteStream);
//    FreeAndNil(LGPBitmap);
  end;
  ShowMessage(LText);
end;
  

編集    削除
Mr.XRAY  2025-04-14 12:58:56  No: 151935  IP: [192.*.*.*]

>    //    FreeAndNil(LGPBitmap);  

ゴメンなさい.コメントを削除してください.
  

編集    削除
vram  2025-04-14 15:08:39  No: 151936  IP: [192.*.*.*]

説明の内容も環境も、あと出てくる用語もゴチャゴチャなので余計なところで躓いて解決しづらいですね。

そもそもdelphi 10.1 を使っているなら Windows10 64bitの方で開発して 64bitと32bitで実行結果が変わるのか試してみて下さい

もし開発環境がWindows7 32bitにしかなくて 64bitでコンパイルした実行ファイルをWindows10に持っていったらファイルが上手く動かない
だったら環境が変わるので出力先の指定が違うとかOSの違いで書き込めない(書き込めたように見えて別の所にある)とかも考えられます

もし 2の画像部分の位置が 32bitと64bitで違っているならそれが原因では?

それと 3の処理でGPBitmap に入っている画像を表示させたら正常に表示されるのですか?

全体的な処理では一時ファイルだろうけど
サンプルだと最後の3の処理でのファイル出力になるからここでは普通に「ファイル」でいいんじゃね?
一時ファイルと言われたらいちいちその場所探しにってしまうし

編集    削除
Mr.XRAY  2025-04-14 15:47:43  No: 151937  IP: [192.*.*.*]

> 一時ファイルと言われたらいちいちその場所探しにってしまうし

TGPBitmap を操作すると一時ファイルが出来るのか ?
どこに ? Windows の一時ファイル ?
で,分からないので,私の提示したコードは説明なししました.
それと,ImageBMPClsid は以下のように定義してください.

const
  ImageBMPClsid : TGUID = '{557CF400-1A04-11D3-9A73-0000F81EF32E}';

編集    削除
Mr.XRAY  2025-04-14 18:19:34  No: 151938  IP: [192.*.*.*]

読み出した LByteStream には,32bit の TBitmap のデータが格納されています.
したがって,以下のようにして TImage に描画できます.

      LByteStream.Position := 0;
      Image1.Picture.Bitmap.LoadFromStream(LByteStream);
      Image1.Picture.Bitmap.AlphaFormat := afDefined;
  
 

編集    削除
onodat  2025-04-14 19:34:04  No: 151939  IP: [192.*.*.*]

有難うございます。
Mr.XRAYさんの「印刷コンポーネント」は活用させて貰っています。

今一度整理します。
1.PictureSectionに 画像ファイル名・画像サイズをファイルに書込み、
画像ファイル(複数のファイル形式あり)は、以下のようにファイル末尾に保存。
[_PictureLinkSection]=2
1,'SANY01010.jpg',(856,249,1156,474),13,42383.74,0,1,1,1,5760054,(0,0),clBlack,clWhite
2,'3-4改造図主蒸気管正面図.tif',(1229,262,1529,842),13,42402.37,0,14,1,1,3639182,(2725,5270),clBlack,clWhite

ここからが画像データ
BM6膰6(@ー膰096/851853:73871653785:;;?D=ELCJSBLV>IW8HU9HX:KX8KX8LW7MX8NY8RY:T[8S]7R\7PZ7PZ9N]8M\:I\:I\;H^<J\?JX7CM3<E09B39@39>59>6;>7<?18;17<1<@1=C*:@)9@);B*9B.=F2BI

2.読み出し時は
  FileStream := TFileStream.Create(LoadFileName, fmOpenRead or fmShareDenyNone);で全体を読み込む。

画像の開始位置も調べてある。
      MemStream := TMemoryStream.Create;
      FileStream.Position := Data[0];//画像開始位置
      MemStream.CopyFrom(FileStream, Data[1]);//画像サイズ

        GPBitmap := TGPBitmap.Create(TStreamAdapter.Create(MemStream, soReference));
        GPBitmap.Save(fName, LImgGUID);//fNameのパスは、EXEファイルのフォルダー下である。

これで、fName(一時ファイル)ができるはずが、64bitではダメ。
ちなみに、  Open時にBackUpファイルをコピーしている。
CopyFile(PChar(LoadFileName), PChar(TmpFileName), False);
これはちゃんと保存されるので、一時ファイルが何故出来ないのか分からない。

直に映像ファイルを読み込めばOKなので、一時ファイルのファイルヘッダーか?
     GPBitmap := TGPBitmap.Create(TStreamAdapter.Create(fName, soReference));

以上

編集    削除
AAAAA  2025-04-14 19:57:07  No: 151940  IP: [192.*.*.*]

     MemStream.CopyFrom(FileStream, Data[1]);//画像サイズ

このMemStream をそのまま Save して 32bit 時と 64bit のファイルを比較してみたら?

編集    削除
Mr.XRAY  2025-04-14 23:38:12  No: 151941  IP: [192.*.*.*]

スレ汚しになるかも知れませんが,
質問者に対するレスではなく,このスレッドを読んでいる方への参考です.
私の提示したコードでは TMemoryStream.Read を使用しています.
質問者のコードで TMemoryStream.CopyFrom が使用されています.
この違いです.

..  TMemoryStream.Read は,
読み込み元のストリームの位置が,読み出した分だけ移動します.
ストリームから連続してデータを読み出す時に使用します.

TMemoryStream.CopyForm は,
読み出し元のストリームの読み出し位置は変わりません.文字通りコピーするだけです.
 コピー元のストリームの読み出し位置を変えたくない時に使用します.
  

編集    削除
vram  2025-04-15 08:58:45  No: 151942  IP: [192.*.*.*]

Mr.XRAYさんがおっしゃっているようにRead ではなくCopyForm を使っているので
正常な画像データにはならず表示されない

というのはわかりますが、当初の質問「がfNameが作成されない。まるで空振り状態」なので別の所に原因があると邪推してしまいました

質問者へのお願いですが減少にたどり着くまでに原因となる要素が複数ありますので
それぞれの段階でファイル化にするなどして、どの段階が原因となっているのかを試して下さい

ファイルはビュアーに頼らずにバイナリーエディタで開き、本当に正しい構造になっているか確認してください
「Stirling.exe」というフリーのバイナリーエディタがあります。古いですがおすすめです

Windowsが標準で用意している画像ビュアーはある程度破損しているファイルでも読み込む場合があります
反対に正常な画像データであっても、いくつかの形式には対応しておらず表示されない場合もあります

画像データを扱うプログラムは簡単なように思えて苦労する場合もありますのでご注意願います

編集    削除
Mr.XRAY  2025-04-15 11:02:31  No: 151943  IP: [192.*.*.*]

またまたスレ汚しです.
[ テキスト + 画像データ] のデータを複数,1 つのファイルとして保存したい場合,
読み出す時に各々の画像データのサイズが必要です.
その場合はは [ テキスト + 画像データのサイズ + 画像データ ] を保存します.
前に提示たコードでは例えば LBmpSize という変数を使用するとすると,
   
      LGPBitmap.Save(TStreamAdapter.Create(LByteStream), ImageBMPClsid);
      // LBmpSize は 読み出しの時の LReadSize と同じ型にする 
      LBmpSize := LByteStream.Size;
      LFileStream.WriteData(LBmpSize);
      LFileStream.Write(LByteStream.Byte+[0], LBmpSize);

読み出す時は以下のようにできます.

      LFileStream.ReadData(LReadSize);
      LByteStream.Size := LReadSize;
      LFileStream.Read(LByteStream.Bytes[0], LReadSize);

      LGPBitmap := TGPBitmap.Create(TStreamAdapter.Create(LByteStream));
      LGPBitmap.Save('503_Save.bmp', ImageBMPClsid);

TBitmap は,そのままファイルストリームに書き込むとサイズが分かりません.
一度ストリームに格納して,そのストリームをファイルストリームに書き込みます
あるいは Position プロパティの値から計算します.

※ クラスにしてしまう方法も考えられますが,可変長データのプロパティの扱いは面倒です.
  

編集    削除
onodat  2025-04-15 17:10:41  No: 151951  IP: [192.*.*.*]

有難うございます。
windows10のversionが「22H2」になって無いのに気付きUpdateをかけたが、改善しなかった。

    MemStream.Position := 0;
    MemStream.SaveToFile(fName2);で作ったファイルを開くと、ファイル形式が違うので読み込めない云々と

そこで調べると、stackoverflow(USA)にTBytesStreamからファイルに書き出すのがあったので
      MemStream := TBytesStream.Create;とし

以下の
TFile.WriteAllBytes(filename, MemStream.Bytes);

あるいは
var
  stream: TBytesStream;
begin
  stream := TBytesStream.Create(Data);
  try
    stream.SaveToFile(FileName);
  finally
    stream.Free;
  end;
後者を使って修正後問題解決しました。

保存はTPictureに行うものなので、直に書き込めるのは無いのでしょうか? これは愚痴デス。
以上、皆さん有難うございました。

編集    削除
vram  2025-04-16 08:11:34  No: 151952  IP: [192.*.*.*]

失礼ですがほかの人が回答しているサンプルとか試して見ました?

「ファイルの中に画像データがあってそこだけ分離させて表示または保存したい」

という質問で

MemStream.Position := 0;

で書き込んだら動きました?
動かないと思いますよ?

愚痴を言いたいのは回答者の方だと思いますが
画像データにjpgデータもあるようですが
jpgデータ形式をそのまま bmp形式としてファイルにしたとかはありませんか?

Windowsの標準ビュアーなら読み込みますが他のビュアーは読み込みませんよ

jpgデータをファイルにしたならヘッダとフッタは固定値になるので
データが壊れていないかわかるかと思います

編集    削除
onodat  2025-04-16 11:02:48  No: 151954  IP: [192.*.*.*]

有難うございます。

(MemStream.SaveToFile(fName2);で作ったファイルを開くと、ファイル形式が違うので読み込めない云々と)
一時ファイル(fName2)は出来るようになったので、これをバイナリエディタで開くとデータ先頭部に空白があるのが分かった。

この空白部分を削除して保存したファイルは、標準ビュアーでもペイントでも開けます。
そこで、読込み開始位置を調べる部分を見直します。

元々の質問は、「画像書き出しの一時ファイルが無いのは何故?」でしたので、画像ファイルが出来さえあれば解決します。

編集    削除
Mr.XRAY  2025-04-18 19:51:26  No: 151959  IP: [192.*.*.*]

改めて説明する必要はないとは思いますが,
私が提示したコードは,EXE が 32bit でも 64bit でも,Windows のビットバージョンが
32bit でも 64bit でも正常に動作します ( 本日,実際に確認 ).

TBytesStream.Bytes[0] はポインタですが,
32bit の EXE では 32bit のポインタ,
64bit の EXE としてコンパイルすれば自動的に 64bit のポインタになります.  
プログラムのコードに 32bit か 64biit かの判定処理は必要ありません.  
なお,64bit の Windows で 32bit の EXE を実行しても 64bit のアプリとしては動作しません.タスクマネージャーで確認できます.
64bit の EXE は 32bit の Windows では実行できません.                                          

では,何故,質問者の場合,Windows のバージョンによって動作が違ったのか ?
それは私には分かりません.私はエスパーではありませんので.

それと,@LpBuffer[0] の先頭の @ はなくても構いません
TBitmap には以下のメソッドがあります.
  TBitmap.SaveToStream(TStream);
TBitmap.LoadFromStream(TStream)
これらのメソッドでは,書き込みと読み出しの位置が自動的に次の位置に移動します.
ですので,TBitmap のインスタンスのデータサイズを知らなくても連続的に書き込みや読み出したができます.

結局,質問のタイトルである 「・・・のは何故?」の疑問には答えることはできませんでした.ご容赦ください.
  

編集    削除
onodat  2025-04-19 20:23:39  No: 151960  IP: [192.*.*.*]

有難うございます。
一時映像ファイルに書き出す、映像データの開始位置が正しくない事が原因と分かりました。

OSのコードページがWindows7は「Shift_Jis=932」、問題のあるWindows10は「Unicode=65001」、ファイルは「utf-16=1200」デス。
Windows10 64bitのマシン「Shift_Jis」では問題無く動く。そこで、バイトベースにて映像データの開始位置取り出す(TByteStream.Bytesを使用)ように修正でOKとなりました。

編集    削除
Mr.XRAY  2025-04-21 14:58:46  No: 151961  IP: [192.*.*.*]

面白そうなので,コードを少し整備してサイトに掲載させていただきました.
http://mrxray.on.coocan.jp/Delphi/Others/SaveTextBitmap.htm

Windows は,2007年1月発売の Windows Vista で全面的に Unicode 化されました.
管理者権限,懐かしいフレーズですね. 
http://mrxray.on.coocan.jp/Delphi/Others/WindowsVista.htm
  
Unicode の処理機能は,Windows 2000 で実装されました.
http://mrxray.on.coocan.jp/Delphi/Others/Izon_Moji.htm#fig2

ですので,掲載したサンプルは,EXE であれば Windows Vista 以降で動作すると思います.
  

編集    削除
onodat  2025-04-21 18:02:27  No: 151962  IP: [192.*.*.*]

有難うございます。
サンプルを勉強させて貰います。

編集    削除
mam  URL  2025-04-22 14:13:35  No: 151963  IP: [192.*.*.*]

20年以上前ですし、ちょっと内容が違うのでスレ汚しと言われそうですが、
私はDelphiで独自のファイルを保存するときには、今のエクセルと同じようにxml形式で保存するようにしました。
画像はBase64エンコードして、適当に改行を入れて保存するようにしました。
どんなバイナリファイルでもBase64エンコードして保存すれば、xml形式で保存出来てしまいます。
ファイルがバイナリ形式だと問題が発生した場合に追いかけるのが大変なのですが、xmlだと追いかけやすいし扱いやすいです。
いざというときはメモ帳で属性等を変更できますし。base64エンコードした画像をコピペで貼り付けることもできますし。
エクセルと同じように、xmlファイルをzip圧縮してしまって独自の拡張子を与えるのもありですし。

Delphiで帳票システムを作らざるを得なくて作ったときのアプリ
https://mam-mam.net/mamdoc/

ご参考まで。

編集    削除
Mr.XRAY  2025-04-22 16:10:15  No: 151964  IP: [192.*.*.*]

> レ汚しと言われそうですが

ハイ,スレ汚しです (笑)
って,私も質問者の疑問とは関係なく書き込みしています (汗)

onodat さん,xml 形式もいいと思いますよ.
画像も BASE64 のデータにして扱えば便利です.

例えば以下の記事中の [URL 欄に貼り付けるコード] をクリックすると,
Javascript のコードが表示されます.
このコードの base64 の部分は数文字だけを格納したフォントの BASE64 のデータです.
この Javascript は,このフォントのコードをフォントに変換して使用しています.

http://mrxray.on.coocan.jp/Delphi/Others/checkFontEnabled.htm#04

編集    削除
onodat  2025-04-22 17:52:18  No: 151965  IP: [192.*.*.*]

有難うございます。
画像データがあるので、圧縮できる「xml形式」は確かに魅力です。
「xml形式」は思い付きませんでした、研究してみます。
然しながら、過去ファイルの変換プログラムが必要となりますが...。

編集    削除