4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Delphiでアルファ値やpng,jpgファイルを扱うときはGDI+をお勧めします

Last updated at Posted at 2025-08-02

はじめに

Delphiに関するpngファイルの質問でアルファデータがうまく反映出来ないとう悩みを良くみかけるのですが、そのような問題を解決するには、GDI+がおすすめです
ファイルを読み込む時に自動で画像形式を判別して、Canvasに描画するときもアルファデータを適切に処理します

関連クラス

主なクラスは以下の2つになります。

  • TGPBitmap
  • TGPGraphics

ユニットに以下を追加して使用します

uses
  GDIPAPI, GDIPOBJ, GDIPUTIL;

GDIPUTILはGetEncoderClsid関数を使用しなければ不要です

TGPBitmap

  • 概要
    GDI+ における画像データを表現するクラスです。
    PNG・JPEG・BMP などをサポートし、アルファチャンネル(透過情報)も保持可能。

pngファイルをbmpファイルで保存する例

uses
  GDIPAPI, GDIPOBJ, GDIPUTIL;
var
  bmp: TGPBitmap;
  EncoderClsid: TGUID;
begin
  // ファイル形式から自動で画像種類を判別して画像を読み込む
  bmp := TGPBitmap.Create('sample.png');
  try
    if GetEncoderClsid('image/bmp', EncoderClsid) >= 0 then
      bmp.Save('output.bmp', EncoderClsid)
    else
      raise Exception.Create('BMP encoder not found');
  finally
    bmp.Free;
  end;
end;

TGPGraphics

  • 概要
    GDI+ の描画処理を担うクラスです。キャンバス (TCanvas.Handle) に関連付けて使うことで、文字や図形、画像を高品質に描画可能。
  • 主な用途
    • Delphi の TCanvas に GDI+ の画像を描画する
    • 高解像度やアンチエイリアスを利用した描画
  • 特徴
    • DrawImage を使って TGPBitmap をキャンバスに転送可能
    • 高度な描画オプションを提供(補間モード、スムージングなど)

Canvas に描画する例

uses
  GDIPAPI, GDIPOBJ;

procedure DrawOnCanvas(Canvas: TCanvas; GPBitmap: TGPBitmap; X, Y: Integer);
var
  graphics: TGPGraphics;
begin
  graphics := TGPGraphics.Create(Canvas.Handle);
  try
    graphics.DrawImage(GPBitmap, X, Y);
  finally
    graphics.Free;
  end;
end;

// 使用例
drawOnCanvas(Form1.Canvas, bmp, 10, 10);

保存できるファイル形式

以下のような形式のファイルで保存が可能です。

  • image/bmp
  • image/jpeg
  • image/gif
  • image/tiff
  • image/png


汎用的に使えるようにいくつかの便利な関数を考えました

変換元 変換先 関数
HBITMAP TGPBitmap CreateTGPBitmapFromHBITMAP
TBitmap TGPBitmap CreateTGPBitmapFromTBitmap
TGPBitmap TBitmap CreateTBitmapFromTGPBitmap
クリップボード TGPBitmap CreateTGPBitmapFromClipboard
TGPBitmap クリップボード CopyGPBitmapToClipboard
コードを見る
const
  CLSID_BMP: TGUID = '{557CF400-1A04-11D3-9A73-0000F81EF32E}';
  CLSID_PNG: TGUID = '{557CF406-1A04-11D3-9A73-0000F81EF32E}';
  CLSID_JPG: TGUID = '{557CF401-1A04-11D3-9A73-0000F81EF32E}';
  CLSID_GIF: TGUID = '{557CF402-1A04-11D3-9A73-0000F81EF32E}';
  CLSID_TIF: TGUID = '{557CF405-1A04-11D3-9A73-0000F81EF32E}';
 
uses
  GDIPAPI, GDIPOBJ, Clipbrd;

function CreateTGPBitmapFromHBITMAP(hBmp: HBITMAP): TGPBitmap;
var
  BmpInfo: BITMAPINFO;
  BmpData: TBitmapData;
  Pixels: TArray<ARGB>;
  DC: HDC;
  Width, Height: Integer;
  // アルファ値がない場合、不透明化する
  procedure MakeOpaqueIfNoAlpha();
  var
    i: Integer;
  begin
    for i := 0 to High(Pixels) do
      if GetAlpha(Pixels[i]) <> 0 then Exit;

    for i := 0 to High(Pixels) do
      Pixels[i] := Pixels[i] or $FF000000; // アルファ値にFFを設定
  end;
begin
  DC := CreateCompatibleDC(0);
  try
    FillChar(BmpInfo, SizeOf(BmpInfo), 0);
    BmpInfo.bmiHeader.biSize := SizeOf(BITMAPINFOHEADER);

    // ビットマップの幅と高さを取得
    GetDIBits(DC, hBmp, 0, 0, nil, BmpInfo, DIB_RGB_COLORS);
    Width := BmpInfo.bmiHeader.biWidth;
    Height := Abs(BmpInfo.bmiHeader.biHeight);

    // ピクセルデータを取得
    BmpInfo.bmiHeader.biHeight := -Height; // トップダウン形式に変更
    BmpInfo.bmiHeader.biPlanes := 1;
    BmpInfo.bmiHeader.biBitCount := 32;
    BmpInfo.bmiHeader.biCompression := BI_RGB;
    SetLength(Pixels, Width * Height);
    // ピクセルデータを読み出し
    GetDIBits(DC, hBmp, 0, Height, @Pixels[0], BmpInfo, DIB_RGB_COLORS);
  finally
    DeleteDC(DC);
  end;

  // アルファ値がない場合、不透明化する
  MakeOpaqueIfNoAlpha();

  Result := TGPBitmap.Create(Width, Height, PixelFormat32bppARGB);
  Result.LockBits(MakeRect(0, 0, Width, Height), ImageLockModeWrite, PixelFormat32bppARGB, BmpData);
  try
    // ピクセルデータを書き込み
    Move(Pixels[0], BmpData.Scan0^, Width * Height * 4);
  finally
    Result.UnlockBits(BmpData);
  end;
end;

function CreateTGPBitmapFromTBitmap(Bitmap: TBitmap): TGPBitmap;
begin
  Result := CreateTGPBitmapFromHBITMAP(Bitmap.Handle);
end;

function CreateTBitmapFromTGPBitmap(GPBitmap: TGPBitmap): TBitmap;
var
  hBmp: HBITMAP;
begin
  // 透明の背景色を白色にしてビットマップハンドルを取得
  GPBitmap.GetHBITMAP(MakeColor(255, 255, 255, 255), hBmp);
  Result := TBitmap.Create;
  Result.Handle := hBmp;
end;

function CreateTGPBitmapFromClipboard(): TGPBitmap;
var
  CF_PNG: UINT;
  hMem: HGLOBAL;
  Data: Pointer;
  Stream: TMemoryStream;
begin
  CF_PNG := RegisterClipboardFormat('PNG');
  if Clipboard.HasFormat(CF_PNG) then
  begin
    hMem := Clipboard.GetAsHandle(CF_PNG);
    Data := GlobalLock(hMem);
    try
      Stream := TMemoryStream.Create;
      Stream.WriteBuffer(Data^, GlobalSize(hMem));
      Stream.Position := 0;
      // soOwnedを指定して、Streamの開放はIStream に任せる(Freeは不要)
      Result := TGPBitmap.Create(TStreamAdapter.Create(Stream, soOwned));
    finally
      GlobalUnlock(hMem);
    end;
  end
  else if Clipboard.HasFormat(CF_BITMAP) then
    Result := CreateTGPBitmapFromHBITMAP(Clipboard.GetAsHandle(CF_BITMAP))
  else
    raise Exception.Create('Clipboard does not contain a bitmap');
end;

procedure CopyGPBitmapToClipboard(GPBitmap: TGPBitmap);
var
  CF_PNG: UINT;
  Stream: TStream;
  hMem: HGLOBAL;
  Data: Pointer;
  Bitmap: TBitmap;
begin
  Clipboard.Open;
  try
    Clipboard.Clear;

    //png形式でコピー
    CF_PNG := RegisterClipboardFormat('PNG');
    Stream := TMemoryStream.Create;
    try
      GPBitmap.Save(TStreamAdapter.Create(Stream, soReference), CLSID_PNG);
      Stream.Position := 0;
      hMem := GlobalAlloc(GHND, Stream.Size);
      try
        Data := GlobalLock(hMem);
        try
          Stream.Read(Data^, Stream.Size);
        finally
          GlobalUnlock(hMem);
        end;
        Clipboard.SetAsHandle(CF_PNG, hMem);
      except
        GlobalFree(hMem);
        raise;
      end;
    finally
      Stream.Free;
    end;

    //BITMAP形式でコピー
    Bitmap := CreateTBitmapFromTGPBitmap(GPBitmap);
    try
      Clipboard.Assign(Bitmap);
    finally
      Bitmap.Free;
    end;
  finally
    Clipboard.Close;
  end;
end;

CreateTGPBitmapFromHBITMAP(hBmp: HBITMAP): TGPBitmap

  • 役割
    Windows API の HBITMAP から GDI+ の TGPBitmap を生成する。

  • 引数
    hBmp: HBITMAP — 変換元となるビットマップハンドル。

  • 補足
    もっと簡潔に記述できるメソッドなどが用意されていますが、アルファ値を持つHBITMAPに対してアルファ値を喪失させずに変換するには、この方法が最善でした

CreateTGPBitmapFromTBitmap(Bitmap: TBitmap): TGPBitmap

  • 役割
    Delphi の TBitmap から GDI+ の TGPBitmap を生成する。
  • 引数
    Bitmap: TBitmap — 変換元となる Delphi のビットマップ。

CreateTBitmapFromTGPBitmap(GPBitmap: TGPBitmap): TBitmap

  • 役割
    GDI+ の TGPBitmap から Delphi の TBitmap を生成する。
  • 引数
    GPBitmap: TGPBitmap — 変換元となる GDI+ のビットマップ。

CreateTGPBitmapFromClipboard(): TGPBitmap

  • 役割
    クリップボードの画像データから GDI+ の TGPBitmap を生成する。
    png形式があればpng形式から取得して、アルファデータも保持する

CopyGPBitmapToClipboard(GPBitmap: TGPBitmap)

  • 役割
    指定した GDI+ の TGPBitmap を PNG および BITMAP 形式でクリップボードにコピーする。
  • 引数
    GPBitmap: TGPBitmap — コピーする GDI+ のビットマップ。

クリップボードの画像を PNG 形式で保存するサンプルコード

procedure SaveClipboardImageAsPng(const FileName: string);
var
  GPBitmap: TGPBitmap;
begin
  GPBitmap := CreateTGPBitmapFromClipboard;
  try
    GPBitmap.Save(FileName, CLSID_PNG)
  finally
    GPBitmap.Free;
  end;
end;

// 使用例
begin
  SaveClipboardImageAsPng('clipboard.png');
end;

最後に

過去の私の記事からGDI+でアルファ値を扱ったサンプルプログラムを紹介します

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?