「いろいろな画像ファイルを高速読み書き(GDI+を使用)」
BMP, JPEG, GIF, PNG, TIFF, WMF, EMF 形式の画像ファイルをGDI+で読み込み、
BMP, JPEG, GIF, PNG, TIFF いずれかの形式を指定して書き出す方法。
今回の内容をひとことで表現すれば「GDI+で何でも読み書き」。
0.準備
1.読み込み
2.書き出し
3.まとめ
4.お願いとお断り
0.Delphiを起動して画像の読み書き用Formを準備する
Delphiを起動して、新規にVCLアプリケーションを作成。次のVCLコンポーネントを載せたFormを準備する。準備ができたら、任意のフォルダにプロジェクトを保存する。
(1)FormにStatusBarを1つ置く。
(2)Formの上にPanelを1つ置き、Alignプロパティを「alBottom」、Heightプロパティを「60」に設定。
(3)Panel1をクリックして選択し、Panel1上にButtonを2つ、RadioGroupを1つ置く。名前はデフォルトのまま、CaptionプロパティをButton1は「読み込み」、Button2は「書き出し」、RadioGroup1は「画像の大きさ」に変更。Button2のAnchorsプロパティは下図のようにakTopとakRightのみTrueに変更する。こうすることで、Formを最大化した時、画面右側のButton2がFormの右側下隅(自然な位置)にくる。
(4)Formをクリックして選択し、Form上にScrollBoxを1つ置き、Alignプロパティを「alClient」に設定。 ScrollBox1 をアクティブに(クリックして選択)して、ScrollBox1の上にImageを1つ置く。Imageのプロパティはデフォルト設定のまま。
(5)RadioGroup1のCaptionプロパティを「画像の大きさ」に変更し、Columns(表示するオプションボタンの列数)プロパティに「2」を設定。
さらに、RadioGroup1のItemIndexプロパティを「0」(第1番目のオプションボタンを選択した状態でプログラムが起動する)、Itemsプロパティには1行目に「リサイズ」、2行目に「オリジナル」を設定する。
先に、RadioGroup1のColumnsプロパティを「2」(列)に設定してあるので、1行目の「リサイズ」が1列目に、2行目の「オリジナル」が2列目に表示される。
(6)Formのどこでもよいので、OpenDialogとSaveDialogを1つずつ設置する。これらは非ビジュアルコンポーネントなので、下図のようにして非表示に設定することもできる(設置したことを忘れそうな場合は、非表示にしない方がよいと思う)。
実行(F9)すると C:\ XXX \ プロジェクトファイルのあるフォルダ \Win32\ 内にDebugフォルダが作成されるので、ここに画像を保存する「Data」フォルダを作成する。Dataフォルダ内に、任意の画像データを準備する。
例:画像はJPEG形式で、名前は「Sample.jpg」の場合
1.読み込み
読み込みボタン(Button1)をダブルクリックして、画像ファイルの読み込み手続きを記述する。画面は次のようになる。
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
まず、必要な変数をvar宣言する。このプログラムでは、画像の読み書きにGDI+を利用する。他の方法に比べ、超高速な画像の読み書きが可能である。
GDI(Graphics Device Interface)は、Windowsで画面のグラフィック処理やプリンターへの出力を行う技術で、WindowsXPからその後継となるGDI+が登場し、GDIで不可能であったJPEGやPNGといった画像形式にも対応し、これにより高レベルの2Dグラフィック処理が可能になった。
GDI+を使用するために必要な変数、及び、処理時間計測に必要な変数を、それぞれ次のように宣言する。
procedure TForm1.Button1Click(Sender: TObject);
var
//GDI+を利用する
graphics:TGPGraphics;
bmp:TGPBitmap;
W,H:integer;
//処理時間を計測
timerStart:DWORD;
timerEnd:DWORD;
s:string;
begin
また、GDI+を利用するには、Winapi.GDIPAPI と Winapi.GDIPOBJ をusesに宣言する。また、画像データの保存時に必要なGUIDを自動取得するGetEncoderClsid関数を使用するので、Winapi.GDIPUTILも合わせて宣言する。
さらに、
処理時間計測用に Winapi.MMSystem 、
JPEG形式の画像を使うために Vcl.Imaging.jpeg 、
TPath.GetExtension関数でファイル(Path)名から拡張子を取得するために必要な System.IOUtils もここで同時に uses に追加しておく。
implementation
uses
Vcl.Imaging.jpeg, Winapi.MMSystem,
Winapi.GDIPAPI, Winapi.GDIPOBJ, Winapi.GDIPUTIL,
System.IOUtils;
{$R *.dfm}
読み込みボタン(Button1)をクリックした際に実行される処理を、以下の通り記述する。アイコンの画像以外の、通常使用する画像ファイル(BMP, JPEG, GIF, PNG, TIFF, WMF, EMF 形式)を読み込み可能である。
procedure TForm1.Button1Click(Sender: TObject);
var
//GDI+を利用する
graphics:TGPGraphics;
bmp:TGPBitmap;
W,H:integer;
//処理時間を計測
timerStart:DWORD;
timerEnd:DWORD;
s:string;
begin
//OpenDialogのプロパティはExecuteする前に設定
With OpenDialog1 do begin
//表示するファイルの種類を設定
Filter:='画像ファイル|*.bmp;*.jpg;*.gif;*.png;*.tif;*.emf;*.wmf' +
'|*.bmp|*.bmp' + '|*.jpg|*.jpg' + '|*.gif|*.gif' + '|*.png|*.png' +
'|*.tif|*.tif' + '|*.emf|*.emf' + '|*.wmf|*.wmf';
//データの読込先フォルダを指定
InitialDir:=ExtractFilePath(Application.ExeName)+'Data';
end;
if not OpenDialog1.Execute then Exit; //キャンセルに対応
//時間計測開始
timerStart:=TimeGetTime;
//オブジェクトを生成
bmp:=TGPBitmap.Create(OpenDialog1.FileName);
Image1.Picture.Bitmap.Width:=bmp.GetWidth;
Image1.Picture.Bitmap.Height:=bmp.GetHeight;
Image1.Picture.Bitmap.PixelFormat:=pf24bit;
graphics:=TGPGraphics.Create(Image1.Picture.Bitmap.Canvas.Handle);
try
//イメージを表示
graphics.DrawImage(bmp, 0, 0, bmp.GetWidth, bmp.GetHeight);
//画像の大きさ
case RadioGroup1.ItemIndex of
0:begin
//Image1にリサイズして表示
Image1.Align:=alClient;
W:=Image1.Width;
H:=Image1.Height;
Image1.Width:=W;
Image1.Height:=H;
//Imageに合わせて表示
Image1.AutoSize:=False;
//Imageのサイズに合わせて表示する
Image1.Stretch:=True;
//縦横の比率を変えずに Image のサイズに変更
Image1.Proportional:=True;
end;
1:begin
//オリジナルの大きさで表示
Image1.Align:=alNone;
Image1.AutoSize:=True;
//Imageのサイズに合わせて表示する
Image1.Stretch:=False;
//縦横の比率を変えずに Image のサイズに変更
Image1.Proportional:=False;
end;
end;
//処理時間計測終了
timerEnd:=TimeGetTime;
//計算時間を表示
s:='計算時間:'+(IntToStr(timerEnd-timerStart)+' ms');
StatusBar1.SimpleText:=s;
finally
bmp.Free;
graphics.Free;
end;
end;
処理時間の計測結果をStatusBarに表示するために、FormのCreate時に、以下の設定を行っておく。
procedure TForm1.FormCreate(Sender: TObject);
begin
//StatusBar1の設定(FalseだとStatusBarにテキストが表示されない)
StatusBar1.SimplePanel:=True;
end;
ついでにFormが常に画面の中央に表示されるよう、次のコードをFormのOnShowイベントに記述する。
procedure TForm1.FormShow(Sender: TObject);
begin
//Formを画面の中央に表示
Left:=(Screen.Width-Width) div 2;
Top:=(Screen.Height-Height) div 2;
end;
上書き保存(Ctrl+S)して、実行(F9)。Sample.jpgのファイルサイズは約5.72MBであるが、私のPC環境では最速137msで表示される。最後に旧来の画像読み込み方法も紹介するが、他の方法を使う気にならないくらいGDI+による読み込みはたいへん高速である。
2.書き出し
読み込み手続きの次は、書き出しの手続きを記述する。書き出しボタン(Button2)をダブルクリックして、書き出し手続きを新規に作成する。
procedure TForm1.Button2Click(Sender: TObject);
begin
end;
最初に手続き中で必要な変数をvar宣言する。
procedure TForm1.Button2Click(Sender: TObject);
var
//usesにWinapi.GDIPAPI, Winapi.GDIPOBJが必要
graphics:TGPGraphics;
bmp:TGPBitmap;
//GetEncoderClsid関数の利用とTGUIDを使用するには、
//usesにWinapi.GDIPUTILが必要
ImgGUID:TGUID;
//処理時間を計測
timerStart:DWORD;
timerEnd:DWORD;
s:string;
dotExt, strExt:string; //拡張子を取得する
begin
書き出し処理のコードを記述する。
procedure TForm1.Button2Click(Sender: TObject);
var
//usesにWinapi.GDIPAPI, Winapi.GDIPOBJが必要
graphics:TGPGraphics;
bmp:TGPBitmap;
//GetEncoderClsid関数の利用とTGUIDを使用するには、
//usesにWinapi.GDIPUTILが必要
ImgGUID:TGUID;
//処理時間を計測
timerStart:DWORD;
timerEnd:DWORD;
s:string;
dotExt, strExt:string; //拡張子を取得する
begin
//OpenDialogのファイル名が空欄ならExit
if OpenDialog1.FileName='' then
begin
ShowMessage('保存する画像がありません!');
Exit;
end;
//SaveDialogのプロパティはExecuteする前に設定しておくこと
With SaveDialog1 do begin
//デフォルトのファイル名を設定
FileName:='Test';
//表示するファイルの種類を設定
Filter:='画像ファイル|*.bmp;*.jpg;*.gif;*.png;*.tif' +
'|*.bmp|*.bmp' + '|*.jpg|*.jpg' + '|*.gif|*.gif' + '|*.png|*.png' +
'|*.tif|*.tif';
//データの読込先フォルダを指定
InitialDir:=ExtractFilePath(Application.ExeName)+'Data';
//拡張子の指定がなかった場合に付加される拡張子を指定
DefaultExt:='jpg';
//上書き保存の確認の設定
Options:=[ofOverWritePrompt];
end;
if not SaveDialog1.Execute then Exit; //キャンセルに対応
//時間計測開始
timerStart:=TimeGetTime;
bmp:=TGPBitmap.Create(OpenDialog1.FileName);
//どちらの指定でも保存可能
//Graphics:=TGPGraphics.Create(Image1.Canvas.Handle);
Graphics:=TGPGraphics.Create(Image1.Picture.Bitmap.Canvas.Handle);
try
//90°回転
//bmp.RotateFlip(Rotate90FlipNone);
//画像を取得
Graphics.DrawImage(bmp,0,0);
//拡張子を小文字に変換して取得(.XXX形式:Dotが付いている)
dotExt:=LowerCase(TPath.GetExtension(SaveDialog1.FileName));
//JPEG & TIFFに対応する
if dotExt='.jpg' then begin
strExt:='jpeg';
end else begin
if dotExt='.tif' then begin
strExt:='tiff';
end else begin
strExt:=StringReplace(dotExt, '.', '', [rfReplaceAll, rfIgnoreCase]);
end;
end;
//指定された拡張子を付けて保存
if GetEncoderClsid('image/'+strExt, ImgGUID) >= 0 then
begin
bmp.Save(ChangeFileExt(SaveDialog1.FileName, dotExt), ImgGUID);
end;
//処理時間計測終了
timerEnd:=TimeGetTime;
//計算時間を表示
s:='計算時間:'+(IntToStr(timerEnd-timerStart)+' ms');
StatusBar1.SimpleText:=s;
finally
Graphics.Free;
bmp.Free;
end;
end;
上書き保存(Ctrl+S)して、実行(F9)。読み込みボタンをクリックして画像を読み込んでから、書き出しボタンをクリックして、保存形式(拡張子)を選択、画像ファイル名はデフォルトで「Test」が設定されているので、そのままでよければSaveDialogの「保存」ボタンをクリックして、画像をDataフォルダに保存する。
GDI+による画像ファイルの読み込みと書き出し処理について、次のWebサイトにたいへん詳しく紹介されています。GDI+を学びたい方は必見です。
Delphiを使って、誰かの役に立つプログラムを作成している方なら、誰しもMr.XRAYさんのWebサイトに一度はお世話になっているのではないでしょうか? それくらい貴重な情報が数多く紹介されています。 私自身、これまでに何度助けていただいたことか・・・。Delphiに関する貴重な情報をずっと提供し続けてくださっているMr.XRAYさんに心から感謝申し上げます。
GDI+ 関係サンプル G040_各種の画像形式の表示と変換
URL:http://mrxray.on.coocan.jp/Delphi/GDIPlusSamples/G040_GDIPlus_SomeImageTypes.htm
参考:旧来の画像の呼び出し方法
最も普通に使われてきた(と思われる)JPEG画像の呼び出しの例。GDI+の利用でSample画像は150ms前後で読み込めていたが、こちらの方法では読み込みに1秒近くかかる。
さらに、読み込んだ画像に対して、何か作業を行う場合(例:矩形選択等)は、下のコード内でコメント化してある部分( Image1.Picture.Bitmap.Assign(Jpg); )をアクティブにして、BitmapにAssignしないと、画像加工実行時にエラーになることにも注意。
implementation
uses
Vcl.Imaging.jpeg, Winapi.MMSystem;
//Vcl.Imaging.jpegはJPEGファイルを扱うために必要
//Winapi.MMSystemは計算処理時間の表示用
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Jpg:TJPEGImage;
//処理時間を計測
TStart: DWORD;
TEnd: DWORD;
s:string;
begin
//画像を消去する
Image1.Picture:=nil;
//OpenDialogのプロパティはExecuteする前に設定
With OpenDialog1 do begin
//表示するファイルの種類を設定
Filter:='JPEG Files (*.jpg, *.jpeg)|*.jpg;*.jpeg';
//データの読込先フォルダを指定
InitialDir:=ExtractFilePath(Application.ExeName)+'Data';
end;
if not OpenDialog1.Execute then Exit; //キャンセルに対応
//オブジェクトを生成
jpg := TJPEGImage.Create;
try
//時間計測開始
TStart:=TimeGetTime;
//ファイルから読み込み
jpg.LoadFromFile(OpenDialog1.FileName);
//Image1に(メモリから)表示
Image1.Picture.Assign(jpg);
//Image1.Picture.Bitmap.Assign(Jpg);
//処理時間計測終了
TEnd:=TimeGetTime;
//計算時間を表示
s:='計算時間:'+(IntToStr(TEnd-TStart)+' ms');
StatusBar1.SimpleText:=s;
finally
//オブジェクトを破棄
jpg.Free;
end;
end;
3.まとめ
これまでいろいろな形式の画像を読み込んだり、書き込んだりする場合は、それぞれの画像形式に合わせてプログラムコードを用意していたが、GDI+を利用すればあらゆる場合に対応できることがわかった。かつ、処理速度も超高速で快適に使用できる。
4.お願いとお断り
このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。
【関連記事】
- Checked プロパティのみ設定したい! 2024年12月8日
- DelphiのSmart CodeInsightを使ってみました! 2024年11月2日
- それは冷蔵庫のドアの警報音じゃなくて・・・ 2024年10月21日
- Excelのプロセスを終了させる 2024年10月14日
- デジタル採点 All in One ! 2024年9月29日
- [dcc32 致命的エラー] F2039 ファイル ‘.\Win32\Release\project1.exe’ を作成できません 2024年9月14日
- StringGridのデータを印刷プレビューして印刷 2024年8月18日
- 引き返す勇気 2024年7月29日
- 塗りつぶさないマークシート 2024年7月7日
- 100選択肢対応マークシートを使用した試験の実施方法 2024年6月28日