実行結果
解説
画像は点(ドット)の集まりで構成されていて、その各ドットの色要素が(RGBα)の場合、α(透過度)を徐々に変化させれば徐々に透けていく絵が表示できます。
2つの画像を用意して、一方はαの値を255→0(無透過→透明)、もう一方を0→255(透明→無透過)に徐々に変化させ、この2つの画像を重ね合わせれば、画像の変身(簡易モーフィング)が実現します。
GDI+を使えば、画像を構成するすべてのドットのα値の変更を簡単に実現させることができます。
その方法は以下の行列式の演算で行えます。
\begin{pmatrix}
R,& G,& B,& α,& 1
\end{pmatrix}
\begin{pmatrix}
1,&0,&0,&0,&0 \\
0,&1,&0,&0,&0 \\
0,&0,&1,&0,&0 \\
0,&0,&0,&透過率,&0 \\
0,&0,&0,&0,&1 \\
\end{pmatrix}
=>
\begin{pmatrix}
R,& G,& B,& α \times 透過率,& 1
\end{pmatrix}
詳しくは以下の記事が参考になります。
プログラム
Delphiでコーディングしたら以下のようになります
参考元
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, GDIPOBJ, GDIPAPI;
type
TForm1 = class(TForm)
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Image1Click(Sender: TObject);
private
FGPBitmap: array[1..2] of TGPBitmap;
procedure Image1Paint(Count: Integer);
public
{ Public 宣言 }
end;
const
FILE1 = 'z:\file1.jpg';
FILE2 = 'z:\file2.jpg';
WAITTIME = 10; //1回ごとのウェイトタイム(ミリ秒)
TIMES = 100; //変身までに何回描画するか
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FGPBitmap[1] := TGPBitmap.Create(FILE1);
FGPBitmap[2] := TGPBitmap.Create(FILE2);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FGPBitmap[1]);
FreeAndNil(FGPBitmap[2]);
end;
procedure TForm1.Image1Click(Sender: TObject);
begin
for var i := 0 to TIMES do begin
Image1Paint(i);
Image1.Repaint;
Sleep(WAITTIME);
end;
var swap := FGPBitmap[1];
FGPBitmap[1] := FGPBitmap[2];
FGPBitmap[2] := swap;
end;
procedure TForm1.Image1Paint(Count: Integer);
const
CM_DEFAULT : TColorMatrix = (
(1, 0, 0, 0, 0),
(0, 1, 0, 0, 0),
(0, 0, 1, 0, 0),
(0, 0, 0, 1, 0),
(0, 0, 0, 0, 1));
var
gcanvas: TGPGraphics;
cm: TColorMatrix;
ia: TGPImageAttributes;
begin
var w := FGPBitmap[1].GetWidth;
var h := FGPBitmap[1].GetHeight;
ia := TGPImageAttributes.Create;
gcanvas := TGPGraphics.Create(Image1.Canvas.Handle);
cm := CM_DEFAULT;
cm[3,3] := Count / TIMES;
ia.SetColorMatrix(cm);
gcanvas.DrawImage(FGPBitmap[1],
MakeRect(0, 0, w, h),
0, 0,
w, h,
UnitPixel,
ia);
cm[3,3] := 1 - cm[3,3];
ia.SetColorMatrix(cm);
gcanvas.DrawImage(FGPBitmap[2],
MakeRect(0, 0, w, h),
0, 0,
w, h,
UnitPixel,
ia);
FreeAndNil(gcanvas);
FreeAndNil(ia);
end;
end.
Githubにソースがあります。
あとがき
GDI+を使えば、複雑な画像の回転・拡大縮小などを行うアフィン変換も容易に実現できるようです。