Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2013年01月10日] [更新 2013年01月10日] GDI+ 関係サンプル
   
GDI+ 関係サンプル 
G180_画像のリージョン
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G180_GDIPlus_ImageToRegion.zip [585 KB] 2013年01月10日版(EXE同梱)







画像の指定色以外の部分をリージョンにする


次の画像の,座標 [0, 0] と同じ色以外の部分をリージョンにするサンプルです.

         

まず,リージョン (TGPRegion) のインスタンスを生成し空にします.画像のピクセルを全て走査して,該当の色でない場合はリージョンに追加していく.という手順で作成しています.
サンプルでは,作成したリージョンを塗りつぶしています.塗り潰す必然性はありませんが,元の画像は,背景色を透過して描画すれば,リージョンを適用した時と同じになってしまいます.

このコードでは,対象の画像のビット深さが 24 ビット以上でないとリージョンは作成できません.



図1
設計時画面
図2
実行結果
  • リージョンを塗りつぶしている

リスト1
画像の指定色以外の部分をリージョンにする
unit Unit1;

interface

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

type
  TMyPanel = class(TCustomPanel)
  protected
    procedure Paint; override;
    function GetRegion(GDPBitmap: TGPBitmap; TransColor: TGPColor;
      GDPRegion: TGPRegion): Boolean;
  end;

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

  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$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     := 5;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;

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

{ TMyPanel }

//-----------------------------------------------------------------------------
//  画像のリージョンを生成する関数
//  引数で指定した色以外の部分を利へジョンにする
//
//  GDPBitmap  : リージョンの作成元画像.この内容は変更しない
//  TransColor : この色以外の部分をリージョンにする
//  GDPRegion  : 呼び出し側で生成したリージョンのインスタンス
//               この関数実行後,この変数にリージョンのデータが格納される
//-----------------------------------------------------------------------------
function TMyPanel.GetRegion(GDPBitmap: TGPBitmap; TransColor: TGPColor;
  GDPRegion: TGPRegion): Boolean;
var
  LWidth     : Integer;
  LHeight    : Integer;
  LBmpData    : TBitmapData;
  LScanLine   : PByte;
  LpQuad      : PRGBQuad;
  LDataStride : Integer;
  LTransR     : Byte;
  LTransG     : Byte;
  LTransB     : Byte;
  LXPos       : integer;
  LYPos       : Integer;
  LRValue     : Byte;
  LGValue     : Byte;
  LBValue     : Byte;
  LColorFlag  : Boolean;
  LRgnRect    : TGPRect;
  LInImage    : Boolean;
begin
  //とりあえず戻り値はFalseにして,リージョンを空にする
  Result := False;
  GDPRegion.MakeEmpty;

  LWidth  := GDPBitmap.GetWidth;
  LHeight := GDPBitmap.GetHeight;

  //リージョン作成の際,除外する色
  //アルファ値を対象外とするためR,LGValue,Bに分解する
  LTransR := GDIPAPI.GetRed   (TransColor);
  LTransG := GDIPAPI.GetGreen (TransColor);
  LTransB := GDIPAPI.GetBlue  (TransColor);

  //ビットマップとしてのデータを取得
  LRgnRect := MakeRect(0, 0, 0, 0);
  if GDPBitmap.LockBits(MakeRect(0, 0, LWidth, LHeight),
                        ImageLockModeWrite,
                        PixelFormat32bppARGB, LBmpData) <> Ok then begin
    exit;
  end;

  //スキャンラインの先頭ポインタを取得
  //Strideは1ラインスキャン当たりのドット(ピクセルではない)
  //画像のピクセルx色成分数
  LScanLine   := LBmpData.Scan0;
  LDataStride := LBmpData.Stride;

  LInImage := False;

  //リージョンの領域矩形(ピクセル数9の初期値は0
  //全ピクセルの色を調査して,指定色以外の時は,そのピクセルを追加して
  //リージョンにUnionする
  LRgnRect := MakeRect(0, 0, 0, 0);
  for LYPos := 0 to LHeight - 1 do begin
    LpQuad := PRGBQuad(LScanLine);

    for LXPos := 0 to LWidth - 1 do begin
      LRValue := LpQuad^.rgbRed;
      LGValue := LpQuad^.rgbGreen;
      LBValue := LpQuad^.rgbBlue;
      LColorFlag :=(LRValue = LTransR) and (LGValue = LTransG) and (LBValue = LTransB);

      if not LInImage then begin
        if not LColorFlag then begin
          LInImage := True;
          LRgnRect.X := LXPos;
          LRgnRect.Y := LYPos;
          LRgnRect.Height := 1;
        end;
      end else begin
        if LColorFlag then begin
          LInImage := False;
          LRgnRect.Width := LXPos - LRgnRect.X;
          GDPRegion.Union(LRgnRect);
        end;
      end;
      Inc(LpQuad);
    end;
    if LInImage then begin
      LInImage := False;
      LRgnRect.Width := LWidth - LRgnRect.X;
      GDPRegion.Union(LRgnRect);
    end;
    Inc(LScanLine, LDataStride);
  end;
  Result := True;

  GDPBitmap.UnlockBits(LBmpData);
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LXLocation   : Single;
  LYLocation   : Single;
  LGPBitmap    : TGPBitmap;
  LGPGraphic   : TGPGraphics;
  LTargetColor : TGPColor;
  LGPImageRgn  : TGPRegion;
  LGPBrush     : TGPBrush;
begin
  inherited;
  if not Assigned(Parent) then exit;

  //描画先のCanvasを設定
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //背景用の画像からTGPBitmapを生成してTMyPanelのCanvasに描画
  LGPBitmap  := TGPBitmap.Create('051946.jpg');
  LGPGraphic.DrawImage(LGPBitmap, 0, 0, Width, Height);


  LGPBitmap.Free;
  //画像ファイルからTGPBitmapのインスタンスを生成
  LGPBitmap  := TGPBitmap.Create('503.bmp');

  //画像の表示位置座標を計算
  LXLocation := (Width  * 1.0 - LGPBitmap.GetWidth)  / 2.0;
  LYLocation := (Height * 1.0 - LGPBitmap.GetHeight) / 2.0;

  //画像の座標(0, 0)の色を除いた部分をリージョンとする
  LGPBitmap.GetPixel(0, 0, LTargetColor);
  LGPImageRgn := TGPRegion.Create;
  GetRegion(LGPBitmap, LTargetColor, LGPImageRgn);

  //表示位置を中央にする
  LGPImageRgn.Translate(LXLocation, LYLocation);

  //リージョン内部をブラシで塗りつぶす
  LGPBrush := TGPSolidBrush.Create(aclGreen);
  LGPGraphic.FillRegion(LGPBrush, LGPImageRgn);

  LGPBitmap.Free;
  LGPGraphic.Free;
  LGPImageRgn.Free;
  LGPBrush.Free;
end;

end.