半透明処理をしてみる


実際に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;
実際の処理はこうなる。CanVasBmpPutBmpを用意し
外部から送られてきたHalfCanvasHalfBmpにコピー。
それから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にあり。

 戻る