TImageから拡張子pngで保存して読み込むとエラーになる

解決


take  2021-11-18 02:51:24  No: 149930  IP: 192.*.*.*

クリップボードからコピーした画像データを拡張子pngで保存してそれを読み込むと
「無効なヘッダが含まれています 破損してるか・・・」
と出てきて読めません

画像編集ソフトから出力するpngファイルは読めて自分で保存したpngが読めないようです。
しかし保存したpngファイルは画像編集ソフトで読めます。

色々 試行錯誤してみると
TPngImageへAssignして保存すると大丈夫みたいなのですが
TImageから保存すると ビットマップ形式のpngファイルになっているのでしょうか?


Windows10 + DelphiXE5

uses PNGImage,Clipbrd;

これは NG
  Image1.Picture.Bitmap.Assign(Clipboard);
  Image1.Picture.SaveToFile('a.png');
  Image1.Picture.LoadFromFile('a.png');

これは OK
  Image1.Picture.Bitmap.Assign(Clipboard);
  Image1.Picture.SaveToFile('b.bmp');
  Image1.Picture.LoadFromFile('b.bmp');

これなら OK
var
 p : TPngImage;
begin
  Image1.Picture.Bitmap.Assign(Clipboard);
  p := TPngImage.Create;
  p.Assign(Image1.Picture.Bitmap);
  p.SaveToFile('c.png');
  Image1.Picture.LoadFromFile('c.png');

編集 削除
Mr.XRAY  2021-11-18 08:29:22  No: 149931  IP: 192.*.*.*

最近 Delphi を始めた方ですね 😁


 // TBitmap に Assign するので TPicture に格納されるのは TBitmap 形式
 Image1.Picture.Bitmap.Assign(Clipboard);
 
  // TPicture には TBitmpa 形式の画像がある
  // 拡張子を変えただけではその拡張子に相当する形式にはならない
  Image1.Picture.SaveToFile('a.png'); 

  // TPicture.LoadFromFile は拡張子で形式を判定する
  // しかし,実際に読み込んだのは TBitmap のファイルなので処理できない
  // したがってエラー
  Image1.Picture.LoadFromFile('a.png'); 
  
  PNG 画像をクリップボードにコピーすると,
  CF_BITMAP, CF_DIB, CF_DIBV5 形式のビットマップもクリップボードに格納されます.
  自分でクリップボードへのコピー形式を設定すれば,PNG だけをコピーできます.

編集 削除
Mr.XRAY  2021-11-18 08:39:41  No: 149932  IP: 192.*.*.*

>  PNG 画像をクリップボードにコピーすると,
>  CF_BITMAP, CF_DIB, CF_DIBV5 形式のビットマップもクリップボードに格納されます.

画像を扱うアプリの中には (特に昔のアプリ) 
クリップボードから PNG 形式の画像を取得できないのがあります.
そんなアプリでも,この仕様によって,ビットマップ画像としては取得できます.  

編集 削除
take  2021-11-18 22:55:45  No: 149933  IP: 192.*.*.*

レスありがとうございます。

>最近 Delphi を始めた方ですね

あ、いえ もう20年以上前からのユーザーです・・・
あの頃 PNGはおろか GifとかJpegも扱えなかったので
おそらくこの分野だけ初心者の知識しか無いかもしれません。
TBitmapを独自拡張して高速描画とかはもう使われなくなってんですねぇ

確認したところ「CF_BITMAP」形式で送られてきていました。
PNGで送られて来ていないうえに
それを不適切な拡張子で保存したのでエラーになるのは当然ですね。

勉強になりました。

CF_BITMAP形式でしか取得出来ないものもあるので
ビットマップとしてTImageに受け取った後
TPngImageにAssignして PNG形式で保存する形式で進めたいと思います。

ありがとうございました。

編集 削除
Mr.XRAY  2021-11-19 07:20:29  No: 149934  IP: 192.*.*.*

今回の現象は,多分,冷静に考えると分かった内容だと思います.
でも一度疑問に思うと
「えっ ? 何故」
とはまってしまうんですよね.
私もよくあります.

ところでご存じだとは思いますが,
ついでに,他の方の参考になるかも知れないので・・・
ファイルの先頭にマジックナンバー と呼ばれる識別値があるファイルがあります
この値を利用して画像ファイルの形式を判定できることがあります.
以下はビットマップと PNG 画像ファイルの例です.

この他にも MIME タイプという値で判定する方法もあります.

//=============================================================================
//  画像ファイルが BMP 形式かを調べる
//  BMP の場合は先頭のの 2 バイトが BM という文字
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStream : TMemoryStream;
  LBiffer : array [0.19] of AnsiChar;
begin
  LStream := TMemoryStream.Create;
  try
    LStream.LoadFromFile('503.bmp');

    FillChar(LBiffer, SizeOf(LBiffer), #0);
    LStream.Position := 0;
    LStream.Read(LBiffer[0], 2);
    
    if SameText('BM', LBiffer) then begin
      ShowMessage(LBiffer);
    end;
  finally
    FreeAndNil(LStream);
  end;
end;

//=============================================================================
//  画像ファイルが PNG 形式かを調べる
//  PNG の場合は 2 バイト目から PNG という文字になっている
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LStream : TMemoryStream;
  LBiffer : array [0..19] of AnsiChar;
begin
  Memo1.Lines.Clear;

  LStream := TMemoryStream.Create;
  try
    LStream.LoadFromFile('503.png');

    FillChar(LBiffer, SizeOf(LBiffer), #0);
    LStream.Position := 1;
    LStream.Read(LBiffer[0], 3);
    
    if SameText('PNG', LBiffer) then begin
      ShowMessage(LBiffer);
    end;
  finally
    FreeAndNil(LStream);
  end;
end;

編集 削除
take  2021-11-19 07:57:44  No: 149935  IP: 192.*.*.*

大変参考になります。

今回ファイルの判断方法として
CreateOleObject('Shell.Application');にて
GetDetailsOf(n,m);
を使用していたのですが

その中のファイルの種類を取得すると PNGが返って来ていたのも原因でした。

こういう所でつまづくと
気になって開発が止まってしまいますね

編集 削除
Mr.XRAY  2021-11-19 13:24:15  No: 149936  IP: 192.*.*.*

> CreateOleObject('Shell.Application');

おっ,使っていますね.これ便利です.

編集 削除