|
Delphi Programming / Object Pascal
[掲載 2012年08月20日] [更新 2017年05月17日] Delphi サンプルプログラム集 |
156_ビットマップ画像のオーバレイ表示とアルファ値処理 |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro |
|
156_BitmapOverlayDraw.zip [3,992 KB] 2017年05月17日版 (EXE 同梱) |
- 2013年02月13日
- TImage.Picture.Bitmap.Canvas.Brush.Style := bsClear; の説明を追加
|
複数の画像や図形を表示するという場合,例えば,
- 1 つのコントロール(コンポーネント)に,複数の画像または図形を描画
- 表示用のコントロール (コンポーネント) を複数配置し,その各々に画像または図形を描画
- 1 つまたは複数の画像,図形を描画したビットマップ等をコントロールに表示
- 1 つまたは複数の画像,図形を描画したコントロールを,複数表示するレイヤー構造
- TStringGrid の各セルやTListView の各 Item に 1 つまたは複数の画像を表示
のような形式があります.また,これらの形式を組み合わせることも考えられます.特に,2 番目の形式は,個々の画像あるいは図形を移動,リサイズする機能を実装する際に使用されます.
ここでは,最初の形式で,TImage に複数のビットマップ画像を表示する場合をテストしてみました.つまり,既に表示している画像に,別の画像を重ね描画,オーバレイするテストです.
このような画像処理に関するサンプルは,例えば,Torry's DELPHI に画像効果,処理関係のコンポーネントが登録されています.アルファブレンドを実装した Graphics32 には,多くのアドオンが提供されていて,機能を追加して利用できます.中村拓男さんも,DHGL というグラフィックスライブラリを提供しています. |
TImage に 2 つのビットマップ画像を表示します.
下の図は,ディスク内のビットマップ画像のファイルを読み込んで表示した例です.画像には背景があります.画像サイズというのは,この背景を含めた矩形領域の大きさです.アルファチャンネル付きの画像で,透明部分を持っているものや,リージョンやパスで定義する図形もありますが,通常は背景にも色があります.
したがって,そのまま描画すると,図のように,この矩形領域が重なって,前に描画した画像の一部が見えなくなることがあります. |
|
図1
設計時画面
|
|
図2
[Button1]クリック
- 上が元のビットマップ画像
- ビットマップに何の操作なしで表示した場合
- 既に表示・描画されている画像の一部が隠れている
|
この現象を避けるには,表示するビットマップの Transparent プロパティをTrue にします.
すると,ビットマップの指定した色の部分が透過して表示されます.この色はビットマップの「透過色」で,デフォルトではビットマップの左下のピクセルの色が使用されます.
このプロパティを設定して実行した結果が下の図です. |
|
図3
[Button2]クリック
- 上が元のビットマップ画像
- TBitmap の Transparent を Trueにした場合
- 透過してビットマップの背景が見える
|
Delphi 関係のコミュニティの場の発言を読むと,TImage も透過表示したい場合があるようです.
その場合は,TImage の Transparent プロパティの値を True にします.その結果が下の図です.ところがこの図では,ビットマップの白の部分も透けてしまっています.
TImage に Draw 等の描画関係のメソッドを実行すると,
- TImage と同じサイズのビットマップが生成される
- TImage.Canvas への描画は,そのビットマップの Canvas に描画することと同じ
- その結果,TImage と同じサイズの 1 つのビットマップとなる
- したがって,TImage からビットマップを取り出すと,このサイズとなる
- このビットマップは,それまでの描画結果のビットマップである
- このビットマップには,描画した画像を個別に区別する情報は存在しない
- ピクセル単位の色とアルファ値の情報(サポートしている場合)は取得できる
ということになります.最後の 3 つは TImage に限らず,全てのビットマップに共通です.
この TImage の Canvas のビットマップの背景色のデフォルトは白です.したがって,透過するというのは,白が透過するためこの現象が発生します.
Assign で画像を代入した場合は,この現象は発生しません.この時 TImage に生成されるビットマップのサイズは,元の画像のビットマップのサイズとなります.
Assign した後に,Draw メソッド等で別の画像を描画しても,このビットマップのサイズは変わりません.既に TImage にはビットマップが生成されてしまっているからです.また,TImage のリサイズもできませんが,TImage の Stretch プロパティを True にして描画すればリサイズが可能となります. |
|
図4
[Button3]クリック
- TImage も透過とした場合
- ビットマップの白の部分も透過状態となってしまう
|
画像の「白」の部分を透過させたくない時は,ビットマップの画像には含まれない色で TImage の Canvasを塗り潰します.もちろん,TImage の Transparent プロパティの値は True にします.
この処理をした結果が下の図です. |
|
図5
[Button4]クリック
- TImage の Canvas を他の色で塗り潰した場合
- 他の色は,ビットマップにはない色とする
- ビットマップの白が透過されていない
|
リスト1
TImage に複数のビットマップを描画 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
Button1: TButton;
Image2: TImage;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// ビットマップ画像のファイルを読み込んで
// TImage.Canvas.Draw で描画
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
begin
Image2.Transparent := False;
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//ファイルから画像を読み込んで表示
//元の画像としての表示
LBitmap1.LoadFromFile('501.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
//それを別のTImageに表示
Image2.Canvas.Draw(30, 0, LBitmap1);
//ファイルから画像を読み込んで別のTBitmapとして同じTImageに表示
LBitmap2.LoadFromFile('501.bmp');
Image2.Canvas.Draw(60, 30, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// ビットマップ画像のファイルを読み込んで
// TBitmap.Transparent := True;
// を実行して TImage.Canvas.Draw で描画
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
begin
Image2.Transparent := False;
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//ファイルから画像を読み込んで表示
//元の画像としての表示
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
//透過色部分を透過して描画する指定
LBitmap1.Transparent := True;
LBitmap2.Transparent := True;
//最初のビットマップ画像をTImageに表示
Image2.Canvas.Draw(30, 0, LBitmap1);
//ファイルから画像を読み込んで別のTBitmapとして同じTImageに表示
LBitmap2.LoadFromFile('503.bmp');
Image2.Canvas.Draw(60, 30, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// ビットマップ画像のファイルを読み込んで
// TImage.Transparent := True;
// TBitmap.Transparent := True;
// を実行して TImage.Canvas.Draw で描画
// TImageも透過するが,画像のclWhiteの部分も透けてしまう
// TImage.Transparent := True; の状態で TImageに描画すると,Timageのサイズの
// ビットマップが生成され,その背景がclWhiteになる.この色が透過色となるため
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//ファイルから画像を読み込んで表示
//元の画像としての表示
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
//TImageのCanvasを現在のBrushで塗り潰して透過設定
Image2.Canvas.FillRect(Image2.ClientRect);
Image2.Transparent := True;
LBitmap1.Transparent := True;
LBitmap2.Transparent := True;
//最初のビットマップ画像をTImageに表示
Image2.Canvas.Draw(30, 0, LBitmap1);
//ファイルから画像を読み込んで別のTBitmapとして同じTImageに表示
LBitmap2.LoadFromFile('503.bmp');
Image2.Canvas.Draw(60, 30, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// 上の現象の対策として
// TImageのCanvasを,別の色で塗り潰してから,上と同じ処理を実行
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//ファイルから画像を読み込んで表示
//元の画像としての表示
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
//TImageのCanvasのBrushのColorを指定して塗り潰して透過設定
Image2.Transparent := True;
Image2.Canvas.Brush.Style := bsSolid;
Image2.Canvas.Brush.Color := LBitmap1.TransparentColor;
Image2.Canvas.FillRect(Image2.ClientRect);
LBitmap1.Transparent := True;
LBitmap2.Transparent := True;
//最初のビットマップ画像をTImageに表示
Image2.Canvas.Draw(30, 0, LBitmap1);
//ファイルから画像を読み込んで別のTBitmapとして同じTImageに表示
LBitmap2.LoadFromFile('503.bmp');
Image2.Canvas.Draw(60, 30, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
end.
文字列の場合も同じです.透過状態にするには,透過状態にする処理が必要です.
文字列は,その時の TCanvas の Brush の設定で背景が描画されます.背景を透過するには,次のコードのように,Brush.Style を bsClear にするか,Windows API の SetBkMode 関数で TRANSPARENT を指定します. |
Image2.Canvas.Brush.Style := bsClear;
Image2.Canvas.Font.Color := clGreen;
Image2.Canvas.Font.Size := 40;
Image2.Canvas.TextOut(30, 30, '文字列');
SetBkMode(Image2.Canvas.Handle, TRANSPARENT);
Image2.Canvas.Font.Color := clGreen;
Image2.Canvas.Font.Size := 40;
Image2.Canvas.TextOut(30, 30, '文字列');
|
図6
透過処理をしないで文字列を描画 |
|
図7
透過処理をして文字列を描画 |
02_ビットマップの透過色の設定 - 指定領域内の塗り潰し |
ビットマップの透過色としての背景色をコードで設定する 1 つの方法です.
ビットマップ画像の中には,背景色が設定していないものがあります.この場合はデフォルトの白が背景色になります.TImage の Canvas と同じです.
背景色が白のまま,これを透過色にしてしまうと,ビットマップ画像の白の部分も透過されてしまいます.これを避けるために,背景を他の色に変更する場合に利用できます.
これには,TCanvas の FloodFill という関数を使用します.この関数は,指定したピクセルの色と同じ色のピクセルを,その時の Brush の色で塗り潰します.ただし,指定したピクセルに連続したピクセルしか処理しません.利用には注意が必要です.
ペイント系のアプリケーションで,ユーザの操作によって(座標をマウスでクリックする等して),閉じた図形の内部を塗り潰すのには便利かも知れません. |
|
図8
設計時画面
- 破線は TImage コンポーネント
- 赤いのはTPanelで,クリックするとカラー設定ダイアログを表示
|
|
図9
[Button1]クリック
|
|
図10
[Button2]クリック
|
|
図11
マウス左ボタン押下
|
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
Button1: TButton;
Image2: TImage;
Image3: TImage;
Button2: TButton;
Panel2: TPanel;
ColorDialog1: TColorDialog;
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Panel2Click(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private 宣言 }
FBitmap1 : TBitmap;
FBitmap2 : TBitmap;
FBitmap3 : TBitmap;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// テスト用のビットマップを生成
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FBitmap1 := TBitmap.Create;
FBitmap2 := TBitmap.Create;
FBitmap3 := TBitmap.Create;
end;
//=============================================================================
// フォームを閉じる時の処理
// 生成したビットマップを破棄
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
FreeAndNil(FBitmap1);
FreeAndNil(FBitmap2);
FreeAndNil(FBitmap3);
end;
//=============================================================================
// テスト用のビットマップ画像を読み込んでTimageに表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
FBitmap1.LoadFromFile('501.bmp');
Image1.Picture.Assign(FBitmap1);
FBitmap2.LoadFromFile('502.bmp');
Image2.Picture.Assign(FBitmap2);
FBitmap3.LoadFromFile('503.bmp');
Image3.Picture.Assign(FBitmap3);
end;
//=============================================================================
// 各ビットマップの左下隅ピクセルの色と同色のピクセルの色を変更
// ただし,この処理は,連続したピクセルだけに限られる
//
// 一方,TBitmapのTransparentColorの設定は,画像の同色も透過して処理する
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LFillColor : Cardinal;
LTargetColor : Cardinal;
LPosX : Integer;
LPosY : Integer;
begin
LFillColor := $00FFDD95;
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
if Assigned(FBitmap1) then begin
LPosX := 0;
LPosY := FBitmap1.Height - 1;
LTargetColor := FBitmap1.Canvas.Pixels[LPosX, LPosY];
FBitmap1.Canvas.Brush.Color := LFillColor;
FBitmap1.Canvas.FloodFill(LPosX, LPosY, LTargetColor, fsSurface);
Image1.Picture.Assign(FBitmap1);
end;
if Assigned(FBitmap2) then begin
LPosX := 0;
LPosY := FBitmap2.Height - 1;
LTargetColor := FBitmap2.Canvas.Pixels[LPosX, LPosY];
FBitmap2.Canvas.Brush.Color := LFillColor;
FBitmap2.Canvas.FloodFill(LPosX, LPosY, LTargetColor, fsSurface);
Image2.Picture.Assign(FBitmap2);
end;
if Assigned(FBitmap3) then begin
LPosX := 0;
LPosY := FBitmap3.Height - 1;
LTargetColor := FBitmap3.Canvas.Pixels[LPosX, LPosY];
FBitmap3.Canvas.Brush.Color := LFillColor;
FBitmap3.Canvas.FloodFill(LPosX, LPosY, LTargetColor, fsSurface);
Image3.Picture.Assign(FBitmap3);
end;
end;
//=============================================================================
// カラー設定ダイアログを表示して色を設定
//=============================================================================
procedure TForm1.Panel2Click(Sender: TObject);
begin
if ColorDialog1.Execute then begin
Panel2.Color := ColorDialog1.Color;
end;
end;
//=============================================================================
// Image1, Image2, Image3 のOnMouseDownイベント(共有)
// マウスボタンをダウンした位置と連続するピクセルの色を変更
//=============================================================================
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
LBitmap : TBitmap;
LTargetColor : Cardinal;
begin
if Button = mbLeft then begin
LBitmap := TImage(Sender).Picture.Bitmap;
LTargetColor := LBitmap.Canvas.Pixels[X, Y];
LBitmap.Canvas.Brush.Color := Panel2.Color;
LBitmap.Canvas.FloodFill(X, Y, LTargetColor, fsSurface);
end;
end;
end.
画像の透過処理には別の方法もあります.
それは,該当ピクセルを「透明」にしてしまう方法です.
ビットマップは,色の情報の他に,αチャンネル(アルファチャンネル)というデータを持たせることができます.このアルファチャンネルのアルファ値を操作すると,画像を半透明でオーバレイ表示したり,マスキングや画像の合成処理にも応用できます.
これまで,アルファチャンネルを使用するには,専用のコンポーネント類を使用するしかなかったのですが,Delphi 2009 でこの機能が実装されました.
この機能を使用してビットマップをオーバレイ描画する前に,単一のビットマップで機能を確認してみたのが,以下のサンプルプログラムです.
機能が実装されたと言っても,アルファチャンネルのデータが利用可能になったということで,実際の処理は,ScanLine 等を使用してのピクセル単位の操作が必要です.
アルファチャンネルの機能は,TForm の透明,半透明表示でも使われています.AlphaBlendValue というプロパティがそれです.古いバージョンの Delphi にはこのプロパティはありません. |
|
図12
設計時画面
|
|
図13
[Button1]クリック
- 上は処理前のビットマップ画像
- 左下は,処理後のビットマップ画像だが,アルファチャンネルの表示が無効の状態
- 右下は,処理後のビットマップ画像で,アルファチャンネルを有効にした画像
|
|
図14
[Button2]クリック
- 元のビットマップ画像を半透明にした場合
- 背景は,TImage の Canvas のデフォルトの「白」
- Delphi 2009 よりも前の Delphi で半透明を実現するコードは,ネット上に多くある
|
|
図15
[Button3]クリック
- 処理画像表示用の TImage を透過状態にした場合
- TImage の背景が透過され,Form1 の Canvas が表示されている
|
リスト3
ビットマップ画像の透過部分の透明化
Delphi 2009 以降用 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
Button1: TButton;
Image2: TImage;
Image3: TImage;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
procedure SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// Image1 に元の画像を表示
// Image2 に透明処理(アルファチャンネル処理)をしたビットマップを表示
// Image3 に透明部分を有効にしたビットマップを表示
//
// 以下,全てDelphi 2009以降専用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
LColor : Cardinal;
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//画像をディスクから読み出してImage1に描画
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
//Bitmap1をコピーしたビットマップを作成
LBitmap2.Assign(LBitmap1);
LColor := GetPixel(LBitmap2.Canvas.Handle, 0, LBitmap2.Height - 1);
//アルファ値の処理
SetColorToAlphaZero(LBitmap2, LColor);
//処理したビットマップをImage2に表示
//透明の部分は描画されないので黒
Image2.Canvas.Draw(0, 0, LBitmap2);
//AlphaFormatをafDefinedにするとアルファ値が有効となる
LBitmap2.AlphaFormat := afDefined;
Image3.Canvas.Draw(0, 0, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// 上と同じ処理だが,Delphi 2009から可能となった,透明度を指定した描画
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
LColor : Cardinal;
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//画像をディスクから読み出し,透明度を指定しててImage1に描画
//アルファ値を指定して描画
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1, 150);
//Image1に描画したビットマップと同じビットマップを作成
LBitmap2.Width := LBitmap1.Width;
LBitmap2.Height := LBitmap1.Height;
LBitmap2.Canvas.Draw(0, 0, Image1.Picture.Bitmap);
LColor := GetPixel(LBitmap2.Canvas.Handle, 0, LBitmap2.Height - 1);
//それをアルファ値処理
SetColorToAlphaZero(LBitmap2, LColor);
//処理したビットマップをImage2に表示
//透明の部分は描画されないので黒
Image2.Canvas.Draw(0, 0, LBitmap2);
//AlphaFormatをafDefinedにするとアルファ値が有効となる
LBitmap2.AlphaFormat := afDefined;
Image3.Canvas.Draw(0, 0, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// 最初の処理とほぼ同じ
// 違うのは,TImageのCanvasにDrawではなくTImage.Picture.Assign で表示
// Assign で表示するとTImageの背景は透過状態のまま
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
LColor : Cardinal;
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//画像をディスクから読み出し,Image1のビットマップにAssign
LBitmap1.LoadFromFile('502.bmp');
Image1.Picture.Assign(LBitmap1);
//そのビットマップのコピーを作成
LBitmap2.Assign(LBitmap1);
LColor := GetPixel(LBitmap2.Canvas.Handle, 0, LBitmap2.Height - 1);
//それをアルファ値処理
SetColorToAlphaZero(LBitmap2, LColor);
//処理したビットマップをImage2に表示
//透明の部分は描画されないので黒
Image2.Picture.Assign(LBitmap2);
//AlphaFormatをafDefinedにするとアルファ値が有効となる
LBitmap2.AlphaFormat := afDefined;
Image3.Picture.Assign(LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//-----------------------------------------------------------------------------
// 与えられたビットマップの指定の色のアルファ値を0(透明色)にする関数
//
// ABitmap : 処理対象のビットマップ.処理したビットマップを返す
// TransColor : 透明にする色
//-----------------------------------------------------------------------------
procedure TForm1.SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
type
TRGBQArray = array [0..4095] of RGBQUAD;
PRGBQArray = ^TRGBQArray;
var
LTransR : Byte;
LTransG : Byte;
LTransB : Byte;
LRValue : Byte;
LGValue : Byte;
LBValue : Byte;
LYPos : Integer;
LXPos : Integer;
LScanArray : PRGBQArray;
LColorQuad : RGBQUAD;
begin
ABitmap.Canvas.Lock;
ABitmap.PixelFormat := pf32bit;
//指定色のR,LGValue,Bの各値を取得しておく
LTransR := GetRValue(TransColor);
LTransG := GetGValue(TransColor);
LTransB := GetBValue(TransColor);
for LYPos := 0 to ABitmap.Height - 1 do begin
LScanArray := ABitmap.ScanLine[LYPos];
for LXPos := 0 to ABitmap.Width - 1 do begin
LRValue := LScanArray[LXPos].rgbRed;
LGValue := LScanArray[LXPos].rgbGreen;
LBValue := LScanArray[LXPos].rgbBlue;
if (LTransR = LRValue) and (LTransG = LGValue) and (LTransB = LBValue) then begin
LColorQuad.rgbBlue := 0;
LColorQuad.rgbGreen := 0;
LColorQuad.rgbRed := 0;
LColorQuad.rgbReserved := 0;
LScanArray[LXPos] := LColorQuad;
end else begin
LScanArray[LXPos].rgbReserved := $FF;
end;
end;
end;
ABitmap.Canvas.Unlock;
end;
end.
04_透明化処理をしてビットマップ画像をオーバレイ表示 |
上のサンプルで行った,ビットマップの透過色部分をアルファチャンネルを操作して透明化した複数のビットマップを,TImage にオーバレイ表示します.
使用したビットマップの背景色が異なるビットマップを使用しています. |
|
図16
摂家時画面
|
|
図17
[Button1]クリック
- 上の 2 つは,下の TImage に表示する元の画像
- 下は,透過色の部分を透明処理した結果
|
|
図18
[Button2]クリック
- 2 番目のビットマップを半透明にして,オーバレイで重ねて表示
- TImage の背景色は TImage.Canvas のデフォルトの色である「白」
|
|
図19
[Button3]クリック
- TImage を透過状態にした結果
- TImage の背景が透過されて,Form1 の Canvas が表示されている
|
リスト4
透明化処理をしてビットマップ画像をオーバレイ表示
Delphi 2009 以降用 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
Button1: TButton;
Image3: TImage;
Button2: TButton;
Button3: TButton;
Image2: TImage;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
procedure SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// ビットマップ画像のオーバレイ表示
// 各ビットマップの左下と同じ色を透明処理して表示
//
// 以下,全てDelphi 2009以降専用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
LColor : Cardinal;
begin
Image3.Transparent := False;
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
//最初のビットマップ
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
//次のビットマップ
LBitmap2.LoadFromFile('503.bmp');
Image2.Canvas.Draw(0, 0, LBitmap2);
//最初のビットマップのアルファ値処理
//左下のピクセルの色を透明色とする
LColor := GetPixel(LBitmap1.Canvas.Handle, 0, LBitmap1.Height - 1);
SetColorToAlphaZero(LBitmap1, LColor);
LBitmap1.AlphaFormat := afDefined;
Image3.Canvas.Draw( 30, 10, LBitmap1);
//次のビットマップのアルファ値処理
//左下のピクセルの色を透明色とする
LColor := GetPixel(LBitmap2.Canvas.Handle, 0, LBitmap2.Height - 1);
SetColorToAlphaZero(LBitmap2, LColor);
LBitmap2.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap2);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// 上の処理と違うのは,2番目のDrawの時に,以下のように透明度を指定して
// Image3.Canvas.Draw(60, 40, LBitmap2, 100);
// 半透明で描画
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
LColor : Cardinal;
begin
Image3.Transparent := False;
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
LBitmap2.LoadFromFile('503.bmp');
Image2.Canvas.Draw(0, 0, LBitmap2);
LColor := GetPixel(LBitmap1.Canvas.Handle, 0, LBitmap1.Height - 1);
SetColorToAlphaZero(LBitmap1, LColor);
LBitmap1.AlphaFormat := afDefined;
Image3.Canvas.Draw( 30, 10, LBitmap1);
LColor := GetPixel(LBitmap2.Canvas.Handle, 0, LBitmap2.Height - 1);
SetColorToAlphaZero(LBitmap2, LColor);
LBitmap2.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap2, 100);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//=============================================================================
// 最初の処理と違うのは,以下の2行を追加したただけ
// Image3.Canvas.FillRect(Image3.ClientRect);
// Image3.Transparent := True;
//
// これでTImageが透過状態となる
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LBitmap1 : TBitmap;
LBitmap2 : TBitmap;
LColor : Cardinal;
begin
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
Image3.Picture.Assign(nil);
//現在のBrushで背景を塗り潰す
Image3.Canvas.FillRect(Image3.ClientRect);
Image3.Transparent := True;
LBitmap1 := TBitmap.Create;
LBitmap2 := TBitmap.Create;
try
LBitmap1.LoadFromFile('502.bmp');
Image1.Canvas.Draw(0, 0, LBitmap1);
LBitmap2.LoadFromFile('503.bmp');
Image2.Canvas.Draw(0, 0, LBitmap2);
LColor := GetPixel(LBitmap1.Canvas.Handle, 0, LBitmap1.Height - 1);
SetColorToAlphaZero(LBitmap1, LColor);
LBitmap1.AlphaFormat := afDefined;
Image3.Canvas.Draw( 30, 10, LBitmap1);
LColor := GetPixel(LBitmap2.Canvas.Handle, 0, LBitmap2.Height - 1);
SetColorToAlphaZero(LBitmap2, LColor);
LBitmap2.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap2, 100);
finally
FreeAndNil(LBitmap1);
FreeAndNil(LBitmap2);
end;
end;
//-----------------------------------------------------------------------------
// 与えられたビットマップの指定の色のアルファ値を0(透明色)にする関数
//
// ABitmap : 処理対象のビットマップ.処理したビットマップを返す
// TransColor : 透明にする色
//-----------------------------------------------------------------------------
procedure TForm1.SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
type
TRGBQArray = array [0..4095] of RGBQUAD;
PRGBQArray = ^TRGBQArray;
var
LTransR : Byte;
LTransG : Byte;
LTransB : Byte;
LRValue : Byte;
LGValue : Byte;
LBValue : Byte;
LYPos : Integer;
LXPos : Integer;
LScanArray : PRGBQArray;
LColorQuad : RGBQUAD;
begin
ABitmap.Canvas.Lock;
ABitmap.PixelFormat := pf32bit;
//指定色のR,LGValue,Bの各値を取得しておく
LTransR := GetRValue(TransColor);
LTransG := GetGValue(TransColor);
LTransB := GetBValue(TransColor);
for LYPos := 0 to ABitmap.Height - 1 do begin
LScanArray := ABitmap.ScanLine[LYPos];
for LXPos := 0 to ABitmap.Width - 1 do begin
LRValue := LScanArray[LXPos].rgbRed;
LGValue := LScanArray[LXPos].rgbGreen;
LBValue := LScanArray[LXPos].rgbBlue;
if (LTransR = LRValue) and (LTransG = LGValue) and (LTransB = LBValue) then begin
LColorQuad.rgbBlue := 0;
LColorQuad.rgbGreen := 0;
LColorQuad.rgbRed := 0;
LColorQuad.rgbReserved := 0;
LScanArray[LXPos] := LColorQuad;
end else begin
LScanArray[LXPos].rgbReserved := $FF;
end;
end;
end;
ABitmap.Canvas.Unlock;
end;
end.
次のコードのように,TImage.Canvs.Brush.Style を bsClear にしても TImage の背景を透過することができます.ただし,この設定は,無地のビットマップ画像を「色なし」(非描画)とします.
この「色なし」は Delphi 2009 以上では「黒」で表示されます.この「黒」を透過することになります.したがって,このコードの後に図形や画像を描画すると,予想外の結果となる場合があります.
この結果は Delphi のバージョンによって異なります. |
Image1.Transparent := True;
Image1.Picture.Bitmap.Canvas.Brush.Style := bsClear;
//Image1.Canvas.Brush.Style := bsClear;でも同じ
05_アルファ値を保持したビットマップの保存と読み出し |
ビットマップは TCanvas に半透明で描画できますが,ビットマップそのものを半透明にするにはアルファチャンネルを操作します.画像の各ピクセルのアルファ値に透過度に相当する値を代入します.この値は Byte 値ですから,設定可能な値は 0 から $FF (10 進数で 255) までです.
本サンプルでは,以下のように,半透明にするビットマップと同じサイズのビットマップを別に作成して,そのビットマップ半透明処理しています.元のビットマップに直接半透明の処理を実行すると,ビットマップの設定内容によっては正常に処理できないことがあります.
|
LBitmap := TBitmap.Create;
try
LBitmap.PixelFormat := pf32bit;
LBitmap.SetSize(FTargetBitmap.Width, FTargetBitmap.Height);
LBitmap.Canvas.Draw(0, 0, FTargetBitmap);
透明化または半透明の処理をしたビットマップはアルファチャンネル付きのビットマップということになります.アルファチャンネル付きのビットマップ画像を読み出して TImage 等に描画するには,以下のように,AlphaFormat プロパティの値を afDefined にして,アルファチャンネルを使用していることを指定します.
透明化と半透明化の両方の処理を行う場合は,半透明の処理をしてから透明化処理を実行します.
|
LBitmap := TBitmap.Create;
try
LBitmap.LoadFromFile('503_Alpha.bmp');
LBitmap.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap);
finally
FreeAndNil(LBitmap);
end;
|
図20
設計時の画面 |
|
図21
実行開始時の画面
- 左は処理の確認のために,処理後のビットマップの後方に描画するビットマップ画像
- 右が処理の対象のビットマップ画像
|
|
図22
透明化処理
|
|
図23
半透明化処理
- 半透明の処理を行って保存したビットマップ画像を表示
|
リスト5
透明化または半透明化処理したビットマップ画像を保存して読み出す例
Delphi 2009 以降用 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Image2: TImage;
Image3: TImage;
Button1: TButton;
Button2: TButton;
Button4: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
FReffBitmap : TBitmap;
FTargetBitmap : TBitmap;
FTargetFilePath : string;
procedure SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
procedure SetOpacityToAlphaValue(ABitmap: TBitmap; Opacity: Byte);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// 参考用と操作対象のビットマップ画像をフォーム上部のTImageに描画
//
// 本サンプルコードはDelphi 2009以降専用
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
//操作対象のビットマップ画像ファイルのパス名
// FTargetFilePath := ExtractFilePath(Application.ExeName) + '503.bmp';
FTargetFilePath := ExtractFilePath(Application.ExeName) + '503.bmp';
Image1.Picture.Assign(nil);
Image2.Picture.Assign(nil);
FReffBitmap := TBitmap.Create;
FTargetBitmap := TBitmap.Create;
FReffBitmap.LoadFromFile('502.bmp');
FReffBitmap.AlphaFormat := afDefined;
Image1.Canvas.Draw(0, 0, FReffBitmap);
//操作対象のビットマップ画像を読み込む
FTargetBitmap.LoadFromFile(FTargetFilePath);
FTargetBitmap.AlphaFormat := afDefined;
Image2.Canvas.Draw(0, 0, FTargetBitmap);
end;
//=============================================================================
// フォームは破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FReffBitmap);
FreeAndNil(FTargetBitmap);
end;
//=============================================================================
// ビットマップ画像の透明化処理
// 指定の色のピクセルを透明にする(アルファ値を0にする)
// 処理したビットマップをディスクに保存
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBitmap : TBitmap;
LColor : TColor;
begin
Image3.Picture.Assign(nil);
//左下のピクセルの色を透明色とする
LColor := GetPixel(FReffBitmap.Canvas.Handle, 0, FReffBitmap.Height - 1);
FReffBitmap.TransparentColor := LColor;
FReffBitmap.Transparent := True;
Image3.Canvas.Draw( 30, 10, FReffBitmap);
//---------------------------------------------------------------------------
// 以下,操作対象のビットマップの処理と処理後の表示と保存
//---------------------------------------------------------------------------
LBitmap := TBitmap.Create;
try
LBitmap.PixelFormat := pf32bit;
LBitmap.SetSize(FTargetBitmap.Width, FTargetBitmap.Height);
LBitmap.Canvas.Draw(0, 0, FTargetBitmap);
//左下のピクセルの色を透明色とする
LColor := GetPixel(LBitmap.Canvas.Handle, 0, LBitmap.Height - 1);
SetColorToAlphaZero(LBitmap, LColor);
//処理したビットマップをTImageに描画
//描画の前にAlphaFormatの値をafDefinedにする
LBitmap.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap);
LBitmap.SaveToFile('503_Transe.bmp');
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// ビットマップ画像の半透明化処理
// 各ピクセルのアルファチャンネルの値を指定の値にする
// 処理したビットマップをディスクに保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBitmap : TBitmap;
LColor : TColor;
begin
Image3.Picture.Assign(nil);
//左下のピクセルの色を透明色とする
LColor := GetPixel(FReffBitmap.Canvas.Handle, 0, FReffBitmap.Height - 1);
FReffBitmap.TransparentColor := LColor;
FReffBitmap.Transparent := True;
Image3.Canvas.Draw( 30, 10, FReffBitmap);
//---------------------------------------------------------------------------
// 以下,操作対象のビットマップの処理と処理後の表示と保存
//---------------------------------------------------------------------------
LBitmap := TBitmap.Create;
try
LBitmap.PixelFormat := pf32bit;
LBitmap.SetSize(FTargetBitmap.Width, FTargetBitmap.Height);
LBitmap.Canvas.Draw(0, 0, FTargetBitmap);
//半透明化の処理を実行
SetOpacityToAlphaValue(LBitmap, 100);
//処理したビットマップをTImageに描画
//描画の前にAlphaFormatの値をafDefinedにする
LBitmap.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap);
LBitmap.SaveToFile('503_Alpha.bmp');
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// 透明化処理したビットマップ画像を読み込んで表示
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LBitmap : TBitmap;
LColor : TColor;
begin
Image3.Picture.Assign(nil);
//左下のピクセルの色を透明色とする
LColor := GetPixel(FReffBitmap.Canvas.Handle, 0, FReffBitmap.Height - 1);
FReffBitmap.TransparentColor := LColor;
FReffBitmap.Transparent := True;
Image3.Canvas.Draw( 30, 10, FReffBitmap);
//---------------------------------------------------------------------------
// 以下,処理して保存したビットマップ画像を読み込んでTImageに描画
// 描画の前にAlphaFormatの値をafDefinedにする
//---------------------------------------------------------------------------
LBitmap := TBitmap.Create;
try
LBitmap.LoadFromFile('503_Transe.bmp');
LBitmap.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap);
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// 半透明化処理したビットマップ画像を読み込んで表示
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
LBitmap : TBitmap;
LColor : TColor;
begin
Image3.Picture.Assign(nil);
//左下のピクセルの色を透明色とする
LColor := GetPixel(FReffBitmap.Canvas.Handle, 0, FReffBitmap.Height - 1);
FReffBitmap.TransparentColor := LColor;
FReffBitmap.Transparent := True;
Image3.Canvas.Draw( 30, 10, FReffBitmap);
//---------------------------------------------------------------------------
// 以下,処理して保存したビットマップ画像を読み込んでTImageに描画
// 描画の前にAlphaFormatの値をafDefinedにする
//---------------------------------------------------------------------------
LBitmap := TBitmap.Create;
try
LBitmap.LoadFromFile('503_Alpha.bmp');
LBitmap.AlphaFormat := afDefined;
Image3.Canvas.Draw(60, 40, LBitmap);
finally
FreeAndNil(LBitmap);
end;
end;
//-----------------------------------------------------------------------------
// 与えられたビットマップの透明化処理
// 指定した色のクセルのアルファ値を0にする
//
// ABitmap : 処理対象のビットマップ.処理したビットマップを返す
// TransColor : 透明にする色
//-----------------------------------------------------------------------------
procedure TForm1.SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
type
TRGBQArray = array [0..4095] of RGBQUAD;
PRGBQArray = ^TRGBQArray;
var
LTransR : Byte;
LTransG : Byte;
LTransB : Byte;
LRValue : Byte;
LGValue : Byte;
LBValue : Byte;
LYPos : Integer;
LXPos : Integer;
LScanArray : PRGBQArray;
LColorQuad : RGBQUAD;
begin
ABitmap.Canvas.Lock;
ABitmap.PixelFormat := pf32bit;
//指定色のR,LGValue,Bの各値を取得しておく
LTransR := GetRValue(TransColor);
LTransG := GetGValue(TransColor);
LTransB := GetBValue(TransColor);
for LYPos := 0 to ABitmap.Height - 1 do begin
LScanArray := ABitmap.ScanLine[LYPos];
for LXPos := 0 to ABitmap.Width - 1 do begin
LRValue := LScanArray[LXPos].rgbRed;
LGValue := LScanArray[LXPos].rgbGreen;
LBValue := LScanArray[LXPos].rgbBlue;
if (LTransR = LRValue) and (LTransG = LGValue) and (LTransB = LBValue) then begin
LColorQuad.rgbBlue := 0;
LColorQuad.rgbGreen := 0;
LColorQuad.rgbRed := 0;
LColorQuad.rgbReserved := 0;
LScanArray[LXPos] := LColorQuad;
end else begin
LScanArray[LXPos].rgbReserved := $FF;
end;
end;
end;
ABitmap.Canvas.Unlock;
end;
//-----------------------------------------------------------------------------
// 与えられたビットマップのアルファ値を変更
// つまり半透明の値を設定する
//
// ABitmap : 処理対象のビットマップ.処理したビットマップを返す
// Opacity : 透明度(0 - 透明,255 - 透明なし)
//-----------------------------------------------------------------------------
procedure TForm1.SetOpacityToAlphaValue(ABitmap: TBitmap; Opacity: Byte);
type
TRGBQArray = array [0..4095] of RGBQUAD;
PRGBQArray = ^TRGBQArray;
var
LYPos : Integer;
LXPos : Integer;
LScanArray : PRGBQArray;
begin
ABitmap.Canvas.Lock;
ABitmap.PixelFormat := pf32bit;
for LYPos := 0 to ABitmap.Height - 1 do begin
LScanArray := ABitmap.ScanLine[LYPos];
for LXPos := 0 to ABitmap.Width - 1 do begin
LScanArray[LXPos].rgbReserved := Opacity;
end;
end;
ABitmap.Canvas.Unlock;
end;
end.
06_TCanvas.CopyRect 関数による画像のオーバレイ描画 |
CopyRect 関数を使用すると,画像の (矩形領域の) 一部分を 他の Image や TBitmap の Canvas に描画できます.つまり,画像の重ね描画 (オーバーレイ描画) ができます.
この時,描画先の矩形領域も指定できます.したがって,元の画像を拡大または縮小して,描画先に描画できます.
重ね描画の時に,CopyMode の値と,TImage や TBitmap の Transparent の値によって,描画結果が異なります.参考用にサンプルを添付しておきました.
下図は添付のサンプルの実行結果です.2 つの同じビットマップ画像を重ね描画しています.どちらも同じ大きさで描画しています. |
|
図24
背景がピンクの 2 つの画像を描画
- CopyMode の値は cmSrcCopy
- この値は CopyMode のデフォルト値
|
|
図25
背景がピンクの 2 つの画像を描画
|
|
図26
背景が白の 2 つの画像を描画
|
|