実際にScanLineを
使用し、半透明処理をしてみる
そして使い回しがしやすいよう
色々と付け加えてみる
ScanLineで実際に半透明処理をする場合
その操作の基本は単純である。二つの画像のビットを取り出し
足して2で割ってから元に戻すだけである。
ただしそれを実行するための前準備が色々と必要になる。
その前に、どんなプログラムにするか設計しておこう。
まず、プログラムの途中にいちいち処理を書くのは面倒なので
プロシージャとして独立させることにする。
指定方法は、X・Y・背景画像・合成画像の四つで
画像はBMP形式としておく。例として下のように。
後で改良することも考え、名前に01を付けておく。
HalfDraw01(X,Y,haikei,ufoBmp); |
内部では、それぞれの画像を加工処理用のBMPにコピーし
RBGそれぞれで足して2で割った後、背景画像に転写することに。
procedure TForm1.HalfDraw01(X,Y:Integer;HalfCanvas:TBitmap;HalfBmp:TBitmap); type TRGB = record B,G,R: Byte;end; TRGBLine = array[0..32767] of TRGB; PRGBLine = ^TRGBLine; var ix,jy,Gw,Gh:Integer; CanVasBmp,PutBmp:TBitmap; Line01,Line02:PRGBLine; begin Gw := HalfBmp.Width; Gh := HalfBmp.Height; //初期設定して背景画像コピー CanVasBmp := TBitmap.Create; CanVasBmp.Width := Gw; CanVasBmp.Height:= Gh; CanVasBmp.PixelFormat := pf24bit; //色は24ビット形式で CanVasBmp.Canvas.CopyRect(Rect(0,0,Gw,Gh),HalfCanvas.Canvas, Rect(X,Y,X+Gw,Y+Gh)); //初期設定してキャラクタ画像コピー PutBmp := TBitmap.Create; PutBmp.Width := Gw; PutBmp.Height:= Gh; PutBmp.PixelFormat := pf24bit; //色は24ビット形式で PutBmp.Canvas.Draw(0,0,HalfBmp); //半透明合成 for jy := 0 to Gh-1 do //縦方向の繰り返し begin Line01 := CanVasBmp.ScanLine[jy]; //1ライン読み取り Line02 := PutBmp.ScanLine[jy]; //1ライン読み取り for ix := 0 to Gw-1 do //横方向の繰り返し begin //それぞれの成分を足して2で割る Line01[ix].R := (Line01[ix].R + Line02[ix].R) div 2; Line01[ix].G := (Line01[ix].G + Line02[ix].G) div 2; Line01[ix].B := (Line01[ix].B + Line02[ix].B) div 2; end; end; //合成し終わったものを本体へ転送 HalfCanvas.Canvas.Draw(X,Y,CanVasBmp); //使用済み解放 CanVasBmp.Free; PutBmp.Free; end; |
実際の処理はこうなる。CanVasBmpとPutBmpを用意し
外部から送られてきたHalfCanvasとHalfBmpにコピー。
それからScanLineでそれぞれを読み取り1ピクセルの
RGBそれぞれで合成する。合成し終わってからHalfCanvasへ
書き込み、使い終わった内部のBMPを解放して終了。
以上の経過&実行形式をまとめたものがこちら。
マウスで位置を指定し、クリックすると
UFO.BMPが半透明で描画される。
halfDraw01.lzh
ご利用はご勝手に。ただし責任は各自で。
halfDraw01.exeを実行すれば表示。
DelphiがあればhalfDraw01.dprをダブルクリックすれば
プログラムの内容がそのまま表示されるはず。
上の処理ではUFOの背景である紫の部分も描かれていて
実用とは言えない状態である。それにメインのプログラムで
いちいちTForm1.HalfDraw01を書かなくてはいけないので
この部分を改良してみることにする。
まずは外部ファイル化の設計。外部ファイル化は
以前にJOYSTICKの処理を外部でするようにしたと同じく
オブジェクトとして処理することに決定。
本来ならこういった処理はコンポーネント化という方式で
組み込むのが正しいやり方なのだが、コンポーネントは
作ってからDELPHIに組み込まないといけないのが欠点。
まだ開発中の中途半端なものを組み込むわけにはいかない。
ファイル>新規作成>ユニット で新規立ち上げして
unit Unit1; の部分をunit MixBit;にし、名前を付けて保存。
名前は同じくMixBit。これで前段取りができた。
interface uses Windows,Classes,Graphics; type TRGB = record B,G,R: Byte;end; TRGBLine = array[0..32767] of TRGB; PRGBLine = ^TRGBLine; TMixBit = class(TObject) public procedure HalfDraw02(X,Y:Integer;HalfCanvas:TBitmap;HalfBmp:TBitmap); end; |
interface部は以上のように。
こうしておけば後は普通にプロシージャを書いていくだけでOK。
ただし、外部でのオブジェクト化ということで、実際に使う場所では
オブジェクトとしての処理が必要になる。
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, MixBit; //←MixBitの追加 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 private { Private 宣言 } haikei:TBitmap; ufoBmp: TBitmap; Mix:TMixBit; //←Mixオブジェクトの宣言 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 procedure TForm1.FormCreate(Sender: TObject); begin Mix := TMixbit.Create; //←Mixオブジェクトのクリエイト 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 procedure TForm1.FormDestroy(Sender: TObject); begin Mix.Free; //←Mixオブジェクトの解放 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 //↓使用時には、先端へMixオブジェクトとして宣言 Mix.HalfDraw02(X-16,Y-16,haikei,UfoBmp); |
上では処理をするために内部で宣言したBITMAPに描画していたが
そのたびに生成や解放をしなくてはいけないので
直接指定で計算する方式へ改造してみる。
procedure TMixBit.HalfDraw02(X,Y:Integer;HalfCanvas:TBitmap;HalfBmp:TBitmap); var ix,jy:Integer; CanvasRect,BmpRect : TRect; Line01,Line02,FirstLine:PRGBLine; begin //それぞれの範囲を抽出 CanvasRect := Rect(0,0,HalfCanvas.Width,HalfCanvas.Height); BmpRect := Rect(X,Y,X+HalfBmp.Width,Y+HalfBmp.Height); //重なっている範囲を抽出。重なってなければコピーしないで終了。 if IntersectRect(CanvasRect,CanvasRect,BmpRect) = False then Exit; |
まずは範囲計算用のTRect形式を用意し、コピー範囲を計算する。
これにより描画しない範囲を省くことができる。
IntersectRectはその範囲計算の関数。全く重なっていない場合
Falseを返すので、描画範囲が無い場合に終了させることができる。
FirstLine := HalfBmp.ScanLine[0]; //透明色設定 |
続いて透明色の設定。左下隅の色を透明にするため
最初のラインを取り出しておく。
for jy := CanvasRect.Top to CanvasRect.Bottom -1 do //縦方向の繰り返し begin Line01 := HalfCanvas.ScanLine[jy]; //1ライン読み取り Line02 := HalfBmp.ScanLine[jy - BmpRect.Top]; //1ライン読み取り for ix := CanvasRect.Left to CanvasRect.Right -1 do //横方向の繰り返し begin if (FirstLine[0].R <> Line02[ix -BmpRect.Left].R) or (FirstLine[0].G <> Line02[ix -BmpRect.Left].G) or (FirstLine[0].B <> Line02[ix -BmpRect.Left].B) then begin Line01[ix].R := (Line01[ix].R + Line02[ix -BmpRect.Left].R) div 2; Line01[ix].G := (Line01[ix].G + Line02[ix -BmpRect.Left].G) div 2; Line01[ix].B := (Line01[ix].B + Line02[ix -BmpRect.Left].B) div 2; end; end; end; |
半透明処理の本体での変更は、RECTでの処理が加わったための
数値指定が変わったことと、コピーの際にFirstLine[0]のRGBを
チェックして、半透明色でなければコピーするようにしたこと。
注:if FirstLine[0] <> Line02[ix -BmpRect.Left]という全ビットを
一度に判定する方法は使用できなかった。
とりあえず今はわかりやすいように組んでみたが、判りやすさ優先のため
このプログラムはやや遅く、もっと高速にすることができる。
その方法など、これ以降は個人の学習にまかせるとする。
以上の経過&実行形式をまとめたものがこちら。
マウスで位置を指定し、クリックすると
UFO.BMPが紫色を透明色として半透明コピーされる。
HalfDraw02.lzh
ご利用はご勝手に。ただし責任は各自で。
halfDraw02.exeを実行すれば表示。
DelphiがあればhalfDraw02.dprをダブルクリックすれば
プログラムの内容がそのまま表示されるはず。
合成処理自体はMixBit.pasにあり。