リージョンを作成するAPIとしては,CreateEllipticRgn(楕円形),CreatePolygonRgn(多角形),CreateRectRgn(矩形) およびCreateRoundRectRgn(角の丸い矩形)がある. また,これらを使って作成したリージョンをCombineRgnで結合することもできる.
穴があいていて,そこから背景が覗けるようなウィンドウを作成してみる.
まず,CreateRectRgnでウィンドウのサイズと同じ矩形リージョンを作成する.
次に,CreateEllipticRgnでそれよりも小さな円形リージョンを作成し,
両者をCombineRgnで結合する.CombineRgnのプロトタイプは以下のようになっている.
function CombineRgn(p1, p2, p3: HRGN; p4: Integer): Integer; stdcall;
p4にRGN_XORを指定すると,二つのリージョンを結合した場合に重なり合う部分が除外される. 今の場合は,結合後のリージョンは,最初に作成した矩形リージョンから後で作成した円形リージョンを除外した ものになる. これをSetWindowRgnでウィンドウに選択すれば,円形リージョンの内部はウィンドウ再描画の対象外となり, 背景がそのまま残る. 一旦リージョンをウィンドウに選択すれば,そのリージョンは破棄して構わない.
具体的なコードは以下のようになる.
procedure TForm1.FormCreate(Sender: TObject);
var
RGN1, RGN2: HRGN;
begin
RGN1 := CreateRectRgn( 0, 0, Width, Height );
RGN2 := CreateEllipticRgnIndirect( Rect( 30, 40, 150, 160 ) );
CombineRgn( RGN1, RGN1, RGN2, RGN_XOR );
SetWindowRgn( Handle, RGN1, True );
DeleteObject( RGN2 );
DeleteObject( RGN1 );
end;
今度は複数のリージョンを結合してちょっと複雑なリージョンを作ってみる.
まず,適当なビットマップをFormのOnPaintイベントでCanvasに描画するようなコードを書く.
procedure TForm1.FormCreate(Sender: TObject);
begin
FBitmap := TBitmap.Create;
FBitmap.LoadFromFile( 'hina.bmp' );
ClientWidth := FBitmap.Width;
ClientHeight := FBitmap.Height;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FBitmap.Free;
end;
procedure TForm1.FormResize(Sender: TObject);
var
TheRect: TRect;
begin
TheRect := ClientRect;
InvalidateRect( Handle, @TheRect, False );
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.StretchDraw( Rect( 0, 0, ClientWidth, ClientHeight ), FBitmap );
end;
のようになる.
ただしこれでは面白くも何とも無いので,顔の辺りだけを描画領域に設定する.
OnPaintイベントを以下のように変更する.
procedure TForm1.FormPaint(Sender: TObject);
const Theta = 4e-1 * Pi;
var
R, X, Y, Index: Integer;
Org: TPoint;
Angle: Double;
NewRgn, EntireRgn: HRGN;
begin
Org := Point( 250, 170 );
R := 70;
for Index := 0 to 4 do
begin
Angle := Theta * Index;
X := Round( Org.x + R * Sin( Angle ) );
Y := Round( Org.y - R * Cos( Angle ) );
NewRgn := CreateEllipticRgn( X - R, Y - R, X + R, Y + R );
if Index <> 0 then
begin
CombineRgn( EntireRgn, EntireRgn, NewRgn, RGN_OR );
DeleteObject( NewRgn );
end
else EntireRgn := NewRgn;
end;
SetWindowRgn( Handle, EntireRgn, True );
Canvas.StretchDraw( Rect( 0, 0, ClientWidth, ClientHeight ), FBitmap );
DeleteObject( EntireRgn );
end;
ただし,タイトルバーまで消えてしまうのでドラッグして移動することができない.
そこで,FormのMouseDownイベントに以下のように書いておく.
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if ssLeft in Shift then
begin
ReleaseCapture;
SendMessage( Handle, WM_SYSCOMMAND, SC_MOVE or 2, MakeLong( X, Y ) );
end;
end;
リージョンは領域を表すのに対し,パスは軌跡を表す. BeginPathとEndPathで挟まれたGDI関数の呼び出しがそのDCのパスとして記録されるので. 相当複雑な図形をパスとして記録することも可能である.
しかしながら,SDKのヘルプを見ると, Windows NT/2000ではパスの記録に使えるGDI関数の制限はほとんど無いものの, Windows 9Xでは使えるGDI関数がかなり制限されている.
Windows NT/2000 : AngleArc,LineTo,Polyline,Arc,MoveToEx,PolylineTo, ArcTo,Pie,PolyPolygon,Chord,PolyBezier,PolyPolyline,CloseFigure,PolyBezierTo, Rectangle,Ellipse,PolyDraw,RoundRect,ExtTextOut,Polygon,TextOut
Windows 9X : CloseFigure,ExtTextOut,LineTo,MoveToEx,PolyBezier,PolyBezierTo, Polygon,Polyline,PolylineTo,PolyPolygon,PolyPolyline,TextOut
このように,Windows 9XではEllipseやRectangleが使えないので,かなり不便ではある. 矩形や円はLineToとMoveToを組み合わせて描くしかない.
ただし,パスの記録にはTextOutが使える(これはNT/2000と9Xの両方で使える).
これを利用すれば,中抜き文字や縁取り文字を書くことができる.
例えば,フォームのOnPaintイベントに次のように書く.
procedure TMainForm.FormPaint(Sender: TObject);
var
S: String;
begin
S := 'HINA!';
with Canvas.Font do
begin
Name := 'Arial Black';
Charset := DEFAULT_CHARSET;
Style := Style + [ fsBold ];
Size := 96;
end;
SetBkMode( Canvas.Handle, TRANSPARENT );
BeginPath( Canvas.Handle );
Canvas.TextOut( ( ClientWidth - Canvas.TextWidth( S ) ) div 2,
( ClientHeight - Canvas.TextHeight( S ) ) div 2, S );
EndPath( Canvas.Handle );
Canvas.Pen.Width := 3;
StrokePath( Canvas.Handle );
end;
留意事項としては,
StrokeAndFillPathは輪郭をペンで,内部をブラシで塗りつぶすAPIである.
さらに,パスはリージョンに変換できるので,パスを使えば複雑なリージョンを作成することができる.
(以下製作中)
SEO | [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送 | ||