A Delphi programozási nyelv

Grafikai alapok a Delphihez

A grafikai programozást ma már nagyban leegyszerűsítik a rendelkezésre álló eszközök. Elvárjuk, hogy egy kép formra rajzolásának egyszerű és biztonságos megoldása legyen. A Delphiben a VCL (Visual Component Library) ebben nyújt segítséget előre elkészített komponensek segítségével. Nézzünk meg néhány ilyen komponenst, mielőtt elkezdenénk a példákat.

A Shape komponenssel (Tool palette – Additional - TShape) egyszerű alakzatokat jeleníthetünk meg. Használata könnyű, csak rá kell tenni a formra és megváltoztatni a Brush, Pen és Shape értékeket. A Shape beállításával lehet kört, ellipszist, téglalapot és lekerekített sarkú téglalapot rajzolni. A Brush a háttérszínt, a Pen pedig a határoló vonal szélességét és színét határozza meg.

Az Image komponens képek megjelenítésére használható. Ez a komponens hasznos számos grafikus feladat megoldásában, mint például háttérképek beállítása. A TImage osztály Picture tulajdonsága egy példánya a TPicure osztálynak. A megjeleníteni kívánt képet beállíthatjuk az Object Inspector segítségével a tervezés alatt, de természetesen megadható futási időben is. Például:

Image1.Picture.Bitmap.LoadFromFile('hatter.jpg');

A Strech tulajdonság igazra állításával mindig a teljes képet láthatjuk a komponens méretétől függetlenül. A Center tulajdonság mindig a kép közepét jeleníti meg a komponensben, az AutoSize-zal pedig a komponenst igazíthatjuk a kép méretéhez.

Végül nézzük a PaintBox komponenst. Ha csak egy bizonyos részre akarunk rajzolni, megadhatunk vele egy célterületet. Ebből adódóan a legfontosabb tulajdonsága a PaintBox komponensnek a Canvas (vászon). Ez a tulajdonság a TCanvas osztály egy példánya. A TCanvas osztály nélkülözhetetlen a Delphi VCL grafikában, ezért most vizsgáljuk meg közelebbről.

Eszközkörnyezetek (device context) és a TCanvas osztály

A Windows az eszközkörnyezet kifejezést használja a vászonra, amire rajzolunk. Egy eszközkörnyezettel számos felületre rajzolhatunk, például:

 

·        egy ablak kliens területére vagy egy frame-re

·        az asztalra

·        a memóriába

·        a nyomtatóra vagy más kimeneti eszközre

 

Természetesen léteznek egyéb eszközkörnyezetek is (például menük), de a felsoroltak azok, amikkel a legtöbbször foglalkozunk.

Az API szintjén használni egy eszközkörnyezetet kissé bonyolult lehet. Hiszen először meg kell tanulni, hogyan érhetjük el a Windowsból, majd különböző objektumokat kell hozzárendelnünk (toll (pen), ecset (brush), betű (font)), és csak ezután kezdhetünk neki a rajzolásnak. Ha befejeztük, az eszközkörnyezet törlése előtt fel kell szabadítanunk minden hozzárendelt objektumot, különben azok bennmaradnak a memóriában.

Szerencsére a VCL biztosítja számunkra a TCanvas osztályt, ami kapcsolatot teremt helyettünk az eszközkörnyezettel. Nézzünk egy rövid példát. A következő kód a Windows API-t használja egy piros belsejű, kék szélű kör megrajzolásához:

procedure TForm1.Button1Click(Sender: TObject); var DC : HDC; Brush, OldBrush : HBrush; Pen, OldPen : HPen; begin DC := GetDC(Handle); Brush := CreateSolidBrush(RGB(255, 0, 0)); Pen := CreatePen(PS_SOLID, 1, RGB(0, 0, 255)); OldBrush := SelectObject(DC, Brush); OldPen := SelectObject(DC, Pen); Ellipse(DC, 20, 20, 120, 120); SelectObject(DC, OldBrush); SelectObject(DC, OldPen); ReleaseDC(Handle, DC); end;

Ez a kód ugyan nem olyan vészes, de még így is elfelejthetjük felszabadítani valamelyik objektumot, ha figyelmetlenek vagyunk. Most nézzük meg ugyanezt VCL-lel:

Canvas.Brush.Color := clRed; Canvas.Pen.Color := clBlue; Canvas.Ellipse(20, 20, 120, 120);

Nemcsak rövidebb és olvashatóbb lett a kód, hanem sokkal robosztusabb is. A TCanvas osztály felszabadítja a már nem használt erőforrásokat, így azzal nekünk már nem kell foglalkozni. Ez a módszer egyszerűbb és hatékonyabb, mint az API használata.

A TCanvas osztály sok tulajdonsággal (property) és eljárással rendelkezik. Nézzük meg röviden ezek mire valók:

Fontosabb TCanvas tulajdonságok

tulajdonság

Leírás

Brush

Az ecsetszín vagy minta, amit a területek kitöltésére használunk.

ClipRect

A canvas jelenlegi vágó téglalapja. Csak ebbe a téglalapba lehet rajzolni.

CopyMode

Meghatározza a rajzolás módját (normál, inverz, xor stb.).

Font

A canvas ezzel a betűvel rajzol.

Handle

A canvas leíró tulajdonság. Ezt használjuk a Windows API direkt hívásához.

Pen

Meghatározza a rajzolandó vonalak stílusát és színét.

PenPos

A rajzolás pozíciója x és y koordinátával.

Pixels

Egy tömb a canvas pixeleiből.

Fontosabb TCanvas eljárások

eljárások

Leírás

Arc

A jelenlegi tollal ívet rajzolhatunk.

BrushCopy

Megjelenít egy képet áttetsző háttérrel.

CopyRect

Egy képrészletet másol a megadott helyre.

Draw

A memóriából a megadott helyre másol egy képet.

Ellipse

Ellipszis a jelenlegi tollal és ecsettel.

FloodFill

Kitölt egy területet az ecsettel.

LineTo

A jelenlegi pozícióból húz egy vonalat a megadott koordinátába.

MoveTo

Beállítja a jelenlegi rajzpozíciót.

Pie

Körszeletet rajzol.

Polygon

Kitöltött sokszög rajzolása egy koordináta tömbből.

Polyline

Vonal rajzolása egy koordináta tömbből.

Rectangle

Kitöltött téglalap.

RoundRect

Kitöltött, lekerekített sarkú téglalap.

StretchDraw

Kép másolása a memóriából a megadott helyre. A kép az eredeti méretétől függetlenül ki fogja tölteni a cél téglalapot.

TextExtent

Visszaadja a paraméterben megadott szöveg méretét pixelben. Az érték számításakor mindig a jelenlegi Font beállítást használja.

TextWidth
TextHeight

Visszaadja a paraméterben megadott szöveg szélességét, magasságát pixelben. Az érték számításakor mindig a jelenlegi Font beállítást használja.

TextOut

A jelenlegi betűvel szöveget rajzol a meghatározott helyre.

TextRect

Szöveget rajzol egy megadott téglalapon belülre.

 

Természetesen a most felsorolt tulajdonságok és eljárások a lehetőségek csak kis részét érintik, de az esetek többségében ezek elegendőek a problémánk megoldására. Mielőtt mélyebben megnézzük TCanvas osztályt, először foglalkozzunk a Windows programozásban használatos grafikus objektumokkal.

GDI objektumok

A Windows Grafikus Eszköz Interfész (Graphics Device Interface - GDI) olyan objektumokat tartalmaz, melyek ismerik a különböző eszközkörnyezetek működését. A legismertebb GDI objektumok a tollak, ecsetek és betűk, de ide tartoznak a paletták, bitképek és területek (region) is.

Tollak, ecsetek és betűk

Nézzük meg ezen objektumok működését részletesebben.

Tollak

A toll egy olyan objektum, mellyel vonalat rajzolhatunk. Ez lehet egyszerű vonal, mely két pontot köt össze, vagy téglalapok, ellipszisek, sokszögek széle. A toll a TCanvas osztály Pen tulajdonságán keresztül érhető el. Nézzük a fontosabb tulajdonságait.

A TPen tulajdonságai

tulajdonság

Leírás

Color

A vonal színe.

Handle

A toll leíró tulajdonsága (HPEN). A GDI közvetlen meghívásához használjuk.

Mode

Meghatározza a vonalrajzolás módját (normál, inverz, xor, stb.).

Style

A toll stílusa. Lehet tömör (solid), pontozott (dotted), szaggatott (dashed), dash-dot, clear, stb.

Width

A toll szélessége pixelben

 

Általában ezek a tulajdonságok úgy viselkednek, ahogy az elvárható tőlük. Például így rajzolhatunk egy piros szaggatott vonalat.

 

Canvas.Pen.Color := clRed; Canvas.Pen.Style := psDash; Canvas.MoveTo(20, 20); Canvas.LineTo(120, 20);

A szaggatott és pontozott stílusok csak 1-es szélességű tollal használhatók. A psClear stílus használható a téglalapok, ellipszisek és kitöltött sokszögek szélének eltüntetéséhez.

Ecsetek

Az ecset egy grafikus alakzat kitöltéséért felel. Az ecset lehet több egy egyszerű színnél. Lehet minta (pattern) vagy kép (bitmap) is. A TCanvas osztály Brush tulajdonságának beállításával szabályozhatjuk az aktuális kitöltést, ez egy példánya a TBrush osztálynak.

A TBrush tulajdonságai

tulajdonságok

leírás

Bitmap

Kép beállítása az ecset háttereként.

Color

Ecset színe.

Handle

Az ecset leírója (HBRUSH). Közvetlen GDI híváshoz.

Style

Az ecset stílusa. Lehet tömör (solid), clear, vagy egy a minták (patterns) közül.

 

A stílus alapértelmezett értéke bsSolid. A stílusnak beállítható különböző minták a következők lehetnek: bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, vagy bsDiagCross. A képen látható mintát könnyedén létrehozhatjuk:

 

Canvas.Brush.Color := clBlue; Canvas.Brush.Style := bsDiagCross; Canvas.Ellipse(20, 20, 220, 220);

Másik érdekes lehetőség egy háttérkép beállítása:

 

Canvas.Brush.Bitmap := TBitmap.Create; Canvas.Brush.Bitmap.LoadFromFile('hatter.jpg'); Canvas.Ellipse(20, 20, 220, 220); Canvas.Brush.Bitmap.Free;

A kód első sora létrehoz egy TBitmap objektumot és hozzárendeli az ecset Bitmap tulajdonságához. Erre azért van szükség, mert ez alapbeállításként nem áll rendelkezésünkre. A harmadik sorban megrajzoljuk az ellipszist. Ezután még fel kell szabadítanunk a létrehozott TBitmap objektumot. Mivel mi hoztuk létre, a VCL ezzel nem törődik.

 

Előfordul, hogy „tiszta” ecsetre van szükségünk. Ilyenkor az alakzatok belseje átlátszóvá válik. A „tiszta” ecset használatához, állítsuk a Style-t bsClear-re. Nézzük az előző példát, de most adjunk hozzá egy második kört, aminél ezt az ecsetet használtuk.

 

Canvas.Pen.Width := 1; Canvas.Brush.Bitmap := TBitmap.Create; Canvas.Brush.Bitmap.LoadFromFile('hatter.jpg'); Canvas.Ellipse(20, 20, 220, 220); Canvas.Brush.Style := bsClear; Canvas.Pen.Width := 5; Canvas.Ellipse(70, 70, 170, 170); Canvas.Brush.Bitmap.Free;

Az ecsettel még aprólékosabban dolgozhatunk, ha közvetlenül az API-t használjuk. De az esetek többségében a VCL TBrush osztálya gyorsabb, megbízhatóbb és ugyanúgy alkalmas a feladat megoldására.

Betűk

A betűk használata nagyon egyszerű és az eddigiek elolvasása után ismerős is lesz. TCanvas osztály Font tulajdonságával dolgozunk, amelyet a következőképpen állíthatunk át:

Canvas.Font.Name := 'Courier New'; Canvas.Font.Size := 14; Canvas.Font.Style := Canvas.Font.Style + [fsBold]; Canvas.TextOut(20, 20, 'Teszt');

Ebben a példában a „Teszt” szót „Courier New” betűtípussal, 14-es méretben és félkövéren fogjuk kirajzolni.

Területek (regions) kivágása

A TCanvas osztály ClipRect tulajdonsága éppen a kivágott területet tartalmazza, de ez csak olvasható. A kivágandó terület megváltoztatásához a Windows API-t kell használni. Nézzünk egy példát a vágás használatára, ahol csak a kép egy 200x200 pixeles részletét rajzoljuk ki:

 

var Bitmap : TBitmap; Rgn : HRGN; begin Bitmap := TBitmap.Create; Bitmap.LoadFromFile('hatter.jpg'); Rgn := CreateRectRgn(20, 20, 220, 220); SelectClipRgn(Canvas.Handle, Rgn); Canvas.Draw(0, 0, Bitmap); Bitmap.Free; end;

A kép ezután ugyanazon a helyen fog megjelenni, de csak a 20, 20, 220, 220 koordináták közötti részét láthatjuk. Ennek a területnek (régiónak) nem kell feltétlenül téglalapnak lennie. Ha az előző példában az RGN változónak más értéket adunk, lehet a vágás alakja például kör vagy ellipszis is.

Rgn := CreateEllipticRgn(30, 30, 170, 170);

De megadhatunk bármilyen sokszöget is egy koordináta tömb formájában, erre nézzünk egy példát:

 

const Points : array[0..3] of TPoint = ((X:100;Y:20), (X:20;Y:100), (X:100;Y:180), (X:180;Y:100)); var Bitmap : TBitmap; Rgn : HRGN; begin Bitmap := TBitmap.Create; Bitmap.LoadFromFile('hatter.jpg'); Rgn := CreatePolygonRgn(Points, 4, ALTERNATE); SelectClipRgn(Canvas.Handle, Rgn); Canvas.Draw(0, 0, Bitmap); Bitmap.Free; end;

A CreatePolygonRgn függvény alakítja területté a pontsorozatunkat. Ez a pontsorozat bármilyen hosszú lehet. Nem kell külön jeleznünk a záró koordinátát, mivel függvény automatikusan összekapcsolja az első és utolsó pontot.

Képek megjelenítése

Mint azt már láthattuk, a képek megjelenítése Delphiben elég egyszerű feladat. A TCanvas osztálynak számos eljárása foglalkozik képekkel. A leggyakrabban használt a Draw, mellyel egy meghatározott helyre rajzolhatunk ki képeket. Bár már használtuk korábban, tekintsük meg a kódot, amit bővíteni fogunk:

var Bitmap : TBitmap; begin Bitmap := TBitmap.Create; Bitmap.LoadFromFile('hatter.jpg'); Canvas.Draw(0, 0, Bitmap); Bitmap.Free; end;

Ez a kód létrehoz egy TBitmap objektumot, betölti a megnevezett fájlt, majd megjeleníti azt a form bal felső sarkában. A kép méretének megváltoztatására jó a StretchDraw eljárás. Használatakor megadjuk, mekkora téglalapot töltsön ki a kép:

 

var Bitmap : TBitmap; R : TRect; begin Bitmap := TBitmap.Create; Bitmap.LoadFromFile('hatter.jpg'); R := TRect.Create(20, 20, 300, 220); Canvas.StretchDraw(R, Bitmap); Bitmap.Free; end;

Az egyik legáltalánosabb eljárás a CopyRect. Itt lehetőségünk van nemcsak a cél, hanem a forrás téglalap meghatározására is. Ez lehetővé teszi a kép részekre bontott megjelenítését. Nézzünk erre egy példát:

 

var Bitmap : TBitmap; Src : TRect; Dst : TRect; I, X, Y : Integer; Strips : Integer; Stripsize : Integer; begin Bitmap := TBitmap.Create; Bitmap.LoadFromFile('hatter_s.jpg'); Strips := 6; Stripsize := (Bitmap.Height div Strips); for I := 0 to Pred(Strips) do begin Src := TRect.Create(0, i * Stripsize, Bitmap.Width, (i * Stripsize) + Stripsize); X := Random(Width - Bitmap.Width); Y := Random(Height - Stripsize); Dst := TRect.Create(X, Y, X + Bitmap.Width, Y + Stripsize); Canvas.CopyRect(Dst, Bitmap.Canvas, Src); end; Bitmap.Free; end;

Képrészletek másolása elterjedt grafikus programozási technika. Ha egy nagyobb képet felosztunk kisebb egységekre, a CopyRect eljárással mindig azt a részletet tudjuk megjeleníteni, amelyikre éppen szükségünk van.