Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2012年12月29日] [更新 2012年12月29日] GDI+ 関係サンプル
GDI+ 関係サンプル 
G140_表示中の画像ファイルの操作
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G140_GDIPlus_FileAccess.zip [3,125 KB] 2012年12月29日版 (EXE 同梱)







表示中の画像ファイルの操作


表示中の画像ファイルの操作に関するサンプルです.
表示中の画像ファイルを削除した場合,あるいは削除できないようにしたい場合の参考です.実際にはこれらのサンプルのコードを組合せて使用することもあるかも知れません.

ネット上のコミュニティの場で,「GDI+ を使用すると,表示中の画像のファイルの削除や名前の変更等の編集ができなくなってしまいます」とか,逆に,「GDI+ を使用して画像を表示していても,その画像ファイルが削除できてしまいます」という記事をみかけました.
このページの記事は,これらに対する回答の意味もあります.

GDI+ に限らず,何かの動作をさせたい,という人がいれば,その動作をさせたくないという人もいるということです.どちらの機能も実装すると,利用者は,そのどちらかの動作仕様を選択することになります.そのアプリの設定項目が増えてしまいます.開発するアプリケーションの動作仕様の決定は難しいものです.



[備考]
「できなくなってしまいます」とか「できてしまいます」という表現は,主観的な表現です.科学技術関係では避けたい表現です.
一方,エンドユーザに対しては,製品や作品の特徴を表現する場合,「表示中の元の画像の削除や名前の変更等の操作も可能です」とか,「表示中の元の画像ファイルが変更されてしまうことはありません」等の肯定的な表現も使われます.




01_画像ファイルの操作・削除可能 - 削除すると再描画で非表示


GDI+ 関係のサンプルプログラムは,一部を除いて,多くの場合,この描画コードとなっています.
それは,画像を表示するコントロールの OnPaint イベントあるいは,Paint メソッド内で,画像ファイルを読み込み,そのコントロールの Canvas に描画するものです.

この方法は,他のアプリでも,この画像ファイルを読み込んで表示でき,変更があった場合,再描画すればその変更結果が反映されます.したがって,削除という変更があった場合,描画すべき元の画像がなくなってしまうのですから,再描画すると画像は表示しないことになります.


図1
画像ファイルを表示
図2
画像ファイルを削除して [再描画]
  • 何も表示しなくなる
  • [ファイルの削除] という変更結果が反映されたことになる

リスト1
表示中の画像ファイルを削除できるが,再描画しても画像が表示されない
他のアプリで変更があると,再描画で変更が反映される
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, StdCtrls, GDIPOBJ, GDIPAPI,  GDIPUTIL, ExtDlgs;

type
  TMyPanel = class(TCustomPanel)
  private
    FImageFilePath : String;
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String  read FImageFilePath write SetImageFilePath;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  protected

  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses 
  JPEG, 
  GifImg, 
  pngimage;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  画像ファイル削除可能';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;

    BevelOuter       := bvNone;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;
end;

//=============================================================================
//  [画像ファイル]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  if OpenPictureDialog1.InitialDir = '' then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  end;

  OpenPictureDialog1.Filter := '画像ファイル|*.png;*.jpg;*.jpeg;*.bmp;*.gif';
  if OpenPictureDialog1.Execute then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(MyPanel.ImageFilePath);
    MyPanel.ImageFilePath         := OpenPictureDialog1.FileName;
  end;
end;

//=============================================================================
//  [再描画]ボタン
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if Assigned(MyPanel) then MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FImageFilePath := '';
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定メソッド
//  表示する画像ファイルを指定する
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  if not FileExists(Value) then exit;

  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPImage   : TGPImage;
  LGPGraphic : TGPGraphics;
begin
  inherited;
  if FImageFilePath = '' then exit;

  LGPImage   := TGPImage.Create(FImageFilePath);
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGPGraphic.DrawImage(LGPImage, 0, 0, LGPImage.GetWidth, LGPImage.GetHeight);

  LGPImage.Free;
  LGPGraphic.Free;
end;

end.




02_画像ファイルを削除不可にする


表示中の画像ファイルに対して,削除も含めた変更を禁止する場合です.
この場合は,画像ファイルを読み込んで生成した TGPImage または TGPBitmap のインスタンスをグローバル変数にして,アプリ終了まで保持しておきます.そうすると,他のアプリから,この画像ファイルを変更することはできなくなります.


図3
削除できない

リスト2
表示中の画像ファイルの削除を不可にする
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, StdCtrls, GDIPOBJ, GDIPAPI,  GDIPUTIL, ExtDlgs;

type
  TMyPanel = class(TCustomPanel)
  private
    FGDPImage      : TGPImage;
    FImageFilePath : String;
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property ImageFilePath : String  read FImageFilePath write SetImageFilePath;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  protected

  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses 
  JPEG, 
  GifImg, 
  pngimage;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  画像ファイル削除不可';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;

    BevelOuter       := bvNone;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;
end;

//=============================================================================
//  [画像ファイル]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  if OpenPictureDialog1.InitialDir = '' then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  end;

  OpenPictureDialog1.Filter := '画像ファイル|*.png;*.jpg;*.jpeg;*.bmp;*.gif';
  if OpenPictureDialog1.Execute then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(MyPanel.ImageFilePath);
    MyPanel.ImageFilePath         := OpenPictureDialog1.FileName;
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  FGDPImage := nil;
  FImageFilePath := '';
end;

//-----------------------------------------------------------------------------
//  デストラクタ
//-----------------------------------------------------------------------------
destructor TMyPanel.Destroy;
begin
  if Assigned(FGDPImage) then FreeAndNil(FGDPImage);
  inherited;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定メソッド
//  画像ファイルを読み出して,FGPImageのインスタンスを生成
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  if not FileExists(Value) then exit;

  FImageFilePath := Value;
  if Assigned(FGDPImage) then FreeAndNil(FGDPImage);
  FGDPImage := TGPImage.Create(FImageFilePath);
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic : TGPGraphics;
begin
  inherited;
  if not Assigned(FGDPImage) then exit;

  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGPGraphic.DrawImage(FGDPImage, 0, 0, FGDPImage.GetWidth, FGDPImage.GetHeight);

  LGPGraphic.Free;
end;

end.




03_画像ファイルの削除も変更も可能にする -  TMemoryStream


表示中の画像ファイルに対して,削除を含めた変更を可能にする場合です.
対象の画像ファイルが削除されても,その画像を表示するには,その画像の内容がメモリ上になければなりません.このサンプルでは TMemoryStream を生成して格納します.当然ですが,このメモリ上の操作は,他のアプリからはできません.したがって,他のアプリでの変更は反映されません.

筆者の環境では,このメモリストリームに格納するサンプルは,画像の種類によっては,メモリストリームから読み出す際に例外が発生することがありました.


リスト3
表示中の画像ファイルの削除も変更も可能だが,変更の結果は反映されない
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, StdCtrls, GDIPOBJ, GDIPAPI,  GDIPUTIL, ExtDlgs;

type
  TMyPanel = class(TCustomPanel)
  private
    FImageStream   : TMemoryStream;
    FImageFilePath : String;
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property ImageFilePath : String  read FImageFilePath write SetImageFilePath;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  protected

  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses 
  JPEG, 
  GifImg, 
  pngimage;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  画像ファイルの操作可能';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;

    BevelOuter       := bvNone;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;
end;

//=============================================================================
//  [画像ファイル]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  if OpenPictureDialog1.InitialDir = '' then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  end;

  OpenPictureDialog1.Filter := '画像ファイル|*.png;*.jpg;*.jpeg;*.bmp;*.gif';
  if OpenPictureDialog1.Execute then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(MyPanel.ImageFilePath);
    MyPanel.ImageFilePath         := OpenPictureDialog1.FileName;
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  FImageStream   := TMemoryStream.Create;
  FImageFilePath := '';
end;

//-----------------------------------------------------------------------------
//  デストラクタ
//-----------------------------------------------------------------------------
destructor TMyPanel.Destroy;
begin
  FreeAndNil(FImageStream);
  inherited;
end;

//-----------------------------------------------------------------------------
//  SetParentメソッド処理
//-----------------------------------------------------------------------------
procedure TMyPanel.SetParent(AParent: TWinControl);
begin
  inherited;
  if not Assigned(AParent) then exit;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定メソッド
//  画像ファイルを読み出して,メモリストリームに格納
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  if not FileExists(Value) then exit;

  FImageFilePath := Value;
  FImageStream.Clear;
  FImageStream.LoadFromFile(FImageFilePath);

  //再描画
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//
//  画像ファイルによっては例外が発生する場合あり
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPImage   : TGPImage;
  LGPGraphic : TGPGraphics;
begin
  inherited;
  if FImageStream.Size = 0 then exit;

  //メモリストリームから読み出してTGPImageを生成
  LGPImage := TGPImage.Create(TStreamAdapter.Create(FImageStream, soReference));

  //TMyPanelのCanvasに描画
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGPGraphic.DrawImage(LGPImage, 0, 0);

  LGPGraphic.Free;
  LGPImage.Free;
end;

end.




04_画像ファイルの削除も変更も可能にする  -  TBitmap.Handle


これも,表示中の画像ファイルに対して,削除を含めた変更を可能にする場合です.
このサンプルでは TBitmap に画像を格納し,描画の際,このビットマップのハンドルから TGPBitmap のインスタンスを生成して描画します.
こちらの方が,動作が確実なようです.

リスト4
表示中の画像ファイルの削除も変更も可能だが,変更の結果は反映されない
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, StdCtrls, GDIPOBJ, GDIPAPI,  GDIPUTIL, ExtDlgs;

type
  TMyPanel = class(TCustomPanel)
  private
    FImageBitmap   : TBitmap;
    FImageFilePath : String;
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property ImageFilePath : String  read FImageFilePath write SetImageFilePath;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  protected

  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses 
  JPEG, 
  GifImg, 
  pngimage;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  画像ファイルの操作可能';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;

    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;

    BevelOuter       := bvNone;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;
end;

//=============================================================================
//  [画像ファイル]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  if OpenPictureDialog1.InitialDir = '' then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  end;

  OpenPictureDialog1.Filter := '画像ファイル|*.png;*.jpg;*.jpeg;*.bmp;*.gif';
  if OpenPictureDialog1.Execute then begin
    OpenPictureDialog1.InitialDir := ExtractFileDir(MyPanel.ImageFilePath);
    MyPanel.ImageFilePath         := OpenPictureDialog1.FileName;
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  FImageBitmap := TBitmap.Create;
  FImageFilePath := '';
end;

//-----------------------------------------------------------------------------
//  デストラクタ
//-----------------------------------------------------------------------------
destructor TMyPanel.Destroy;
begin
  FreeAndNil(FImageBitmap);
  inherited;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定メソッド
//  画像ファイルを読み出して,TGPBitmapを生成
//  ビットマップを生成し,そのCanvasにTGPBitmapに格納した内容を描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
var
  LObjWidht  : Integer;
  LObjHeight : Integer;
  LGPImage   : TGPBitmap;
  LGPGraphic : TGPGraphics;
begin
  if not FileExists(Value) then exit;

  FImageFilePath := Value;
  LGPImage := TGPBitmap.Create(FImageFilePath);
  try
    LObjWidht  := LGPImage.GetWidth;
    LObjHeight := LGPImage.GetHeight;

    FImageBitmap.Assign(nil);
    FImageBitmap.SetSize(LObjWidht, LObjHeight);
    LGPGraphic := TGPGraphics.Create(FImageBitmap.Canvas.Handle);
    try
      LGPGraphic.DrawImage(LGPImage, MakeRect(0, 0, LObjWidht, LObjHeight));
      Invalidate;
    finally
      LGPGraphic.Free;
    end;
  finally
    LGPImage.Free;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPImage   : TGPBitmap;
  LGPGraphic : TGPGraphics;
begin
  inherited;

  //ビットマップのハンドルからTGPImageを生成
  LGPImage := TGPBitmap.Create(FImageBitmap.Handle, FImageBitmap.Palette);

  //TMyPanelのCanvasに描画
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGPGraphic.DrawImage(LGPImage, 0, 0);

  LGPImage.Free;
  LGPGraphic.Free;
end;

end.