Developing with Graphics/sk: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Developing with Graphics/sk
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Táto stránky má byť počiatočný postup s ohľadom na manipuláciu s bitmapami a inou grafikou. Keďže nie som programátor grafiky, pozývam všetkých na zdieľanie ich skúseností! Jednoducho pridajte odkaz na ďalšiu sekciu, pridajte stránku a vytvorte svoj vlastný článok WiKi. | |
| 2 | + | ||
| 3 | + | Na tejto stránke budú poskytnuté niektoré všeobecné informácie. | |
| 4 | + | ||
| 5 | + | ## Ďalšie grafické články | |
| 6 | + | ||
| 7 | + | - [[GLScene|GLScene (en)]] - Port vizuálnej OpenGL grafickej knižnice [GLScene](http://www.glscene.org) | |
| 8 | + | - [[TAChart|TAChart (en)]] - Diagramový komponent pre Lazarus | |
| 9 | + | - [[PascalMagick|PascalMagick (en)]] - jednoducho použiteľné API pre prepojenie s [ImageMagick](http://www.imagemagick.org), viacplatformová slobodná sada programov pre vytváranie, úpravu a kompozíciu bitmapových obrázkov. | |
| 10 | + | - [[PlotPanel|PlotPanel (en)]] - Zapisovací a kresliaci komponent pre animované grafy. | |
| 11 | + | - [[LazRGBGraphics|LazRGBGraphics (en)]] - Balíček pre pre rýchle spracovanie a manipuláciu s obrázkami v pamäti (ako scan line). | |
| 12 | + | - [[Perlin Noise|Perlin Noise (en)]] - Článok o používaní Perlin Noise v LCL aplikáciách. | |
| 13 | + | ||
| 14 | + | ## Práca s TBitmap | |
| 15 | + | ||
| 16 | + | Prvá vec, na ktrorú treba pamätať je, že Lazarus je zamýšľaný ako platformovo nezávislý, tak všetky metódy používajúce funkcie Windows API sú mimo. Takže metóda ako ScanLine nie je v Lazarus podporovaná, pretože je určená pre Device Independant Bitmap a používa funkcie z GDI32.dll. | |
| 17 | + | ||
| 18 | + | Zapamätajte si, že ak nezadáte výšku a šírku svojho [[doc:lcl/graphics/tbitmap.html|TBitmap]], bude mať štandardnú veľkosť, ktorá je celkom malá. | |
| 19 | + | ||
| 20 | + | ### Príklad miznutia | |
| 21 | + | ||
| 22 | + | Predstavte si, že chcete vytvoriť miznúci obrázok. V Delphi môžete urobiť niečo také: | |
| 23 | + | ||
| 24 | + | ``` pascal | |
| 25 | + | type | |
| 26 | + | PRGBTripleArray = ^TRGBTripleArray; | |
| 27 | + | TRGBTripleArray = array[0..32767] of TRGBTriple; | |
| 28 | + | ||
| 29 | + | procedure TForm1.FadeIn(aBitMap: TBitMap); | |
| 30 | + | var | |
| 31 | + | Bitmap, BaseBitmap: TBitmap; | |
| 32 | + | Row, BaseRow: PRGBTripleArray; | |
| 33 | + | x, y, step: integer; | |
| 34 | + | begin | |
| 35 | + | Bitmap := TBitmap.Create; | |
| 36 | + | try | |
| 37 | + | Bitmap.PixelFormat := pf32bit; // or pf24bit | |
| 38 | + | Bitmap.Assign(aBitMap); | |
| 39 | + | BaseBitmap := TBitmap.Create; | |
| 40 | + | try | |
| 41 | + | BaseBitmap.PixelFormat := pf32bit; | |
| 42 | + | BaseBitmap.Assign(Bitmap); | |
| 43 | + | for step := 0 to 32 do begin | |
| 44 | + | for y := 0 to (Bitmap.Height - 1) do begin | |
| 45 | + | BaseRow := BaseBitmap.Scanline[y]; | |
| 46 | + | Row := Bitmap.Scanline[y]; | |
| 47 | + | for x := 0 to (Bitmap.Width - 1) do begin | |
| 48 | + | Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5; | |
| 49 | + | Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading | |
| 50 | + | Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5; | |
| 51 | + | end; | |
| 52 | + | end; | |
| 53 | + | Form1.Canvas.Draw(0, 0, Bitmap); | |
| 54 | + | InvalidateRect(Form1.Handle, nil, False); | |
| 55 | + | RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW); | |
| 56 | + | end; | |
| 57 | + | finally | |
| 58 | + | BaseBitmap.Free; | |
| 59 | + | end; | |
| 60 | + | finally | |
| 61 | + | Bitmap.Free; | |
| 62 | + | end; | |
| 63 | + | end; | |
| 64 | + | ``` | |
| 65 | + | ||
| 66 | + | V Lazarus môže byť táto funkcia implementovaná takto: | |
| 67 | + | ||
| 68 | + | ``` pascal | |
| 69 | + | uses LCLType, // HBitmap type | |
| 70 | + | IntfGraphics, // TLazIntfImage type | |
| 71 | + | fpImage; // TFPColor type | |
| 72 | + | ... | |
| 73 | + | procedure TForm1.FadeIn(ABitMap: TBitMap); | |
| 74 | + | var | |
| 75 | + | SrcIntfImg, TempIntfImg: TLazIntfImage; | |
| 76 | + | ImgHandle,ImgMaskHandle: HBitmap; | |
| 77 | + | FadeStep: Integer; | |
| 78 | + | px, py: Integer; | |
| 79 | + | CurColor: TFPColor; | |
| 80 | + | TempBitmap: TBitmap; | |
| 81 | + | begin | |
| 82 | + | SrcIntfImg:=TLazIntfImage.Create(0,0); | |
| 83 | + | SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle); | |
| 84 | + | TempIntfImg:=TLazIntfImage.Create(0,0); | |
| 85 | + | TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle); | |
| 86 | + | TempBitmap:=TBitmap.Create; | |
| 87 | + | for FadeStep:=1 to 32 do begin | |
| 88 | + | for py:=0 to SrcIntfImg.Height-1 do begin | |
| 89 | + | for px:=0 to SrcIntfImg.Width-1 do begin | |
| 90 | + | CurColor:=SrcIntfImg.Colors[px,py]; | |
| 91 | + | CurColor.Red:=(CurColor.Red*FadeStep) shr 5; | |
| 92 | + | CurColor.Green:=(CurColor.Green*FadeStep) shr 5; | |
| 93 | + | CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5; | |
| 94 | + | TempIntfImg.Colors[px,py]:=CurColor; | |
| 95 | + | end; | |
| 96 | + | end; | |
| 97 | + | TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false); | |
| 98 | + | TempBitmap.Handle:=ImgHandle; | |
| 99 | + | TempBitmap.MaskHandle:=ImgMaskHandle; | |
| 100 | + | Canvas.Draw(0,0,TempBitmap); | |
| 101 | + | end; | |
| 102 | + | SrcIntfImg.Free; | |
| 103 | + | TempIntfImg.Free; | |
| 104 | + | TempBitmap.Free; | |
| 105 | + | end; | |
| 106 | + | ``` | |
| 107 | + | ||
| 108 | + | Lazarus kód na tejto stránke bol prevzatý z projektu \$LazarusPath/examples/lazintfimage/fadein1.lpi, takže ak chcete letmý štart s grafickým programovaním pozrite sa na tento príklad. | |
| 109 | + | ||
| 110 | + | ### Kreslenie bitmáp transparentnou farbou | |
| 111 | + | ||
| 112 | + | Nová vlastnosť, implementovaná od Lazarus 0.9.11, sú farebne transparentné bitmapy. Bitmapové súbory (*.BMP) neuchovávajú žiadnu informáciu o priehľadnosti, ale môžu takto pracovať, ak im zvolíte farbu, ktorá bude reprezenzovať priesvitnú oblasť. To je bežný trik používaný v programoch Win32. | |
| 113 | + | ||
| 114 | + | Nasledujúci príklad načíta bitmapu zo zdroja Windows, vyberie farbu pre priehľadnosť (clFuchsia) a potom ho nakreslí na plátno. | |
| 115 | + | ||
| 116 | + | ``` pascal | |
| 117 | + | procedure MyForm.MyButtonOnClick(Sender: TObject); | |
| 118 | + | var | |
| 119 | + | buffer: THandle; | |
| 120 | + | bmp: TBitmap; | |
| 121 | + | memstream: TMemoryStream; | |
| 122 | + | begin | |
| 123 | + | bmp := TBitmap.Create; | |
| 124 | + | ||
| 125 | + | buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID)); | |
| 126 | + | ||
| 127 | + | if (buffer = 0) then exit; // Error loading the bitmap | |
| 128 | + | ||
| 129 | + | bmp.Handle := buffer; | |
| 130 | + | memstream := TMemoryStream.create; | |
| 131 | + | try | |
| 132 | + | bmp.SaveToStream(memstream); | |
| 133 | + | memstream.position := 0; | |
| 134 | + | bmp.LoadFromStream(memstream); | |
| 135 | + | finally | |
| 136 | + | memstream.free; | |
| 137 | + | end; | |
| 138 | + | ||
| 139 | + | bmp.Transparent := True; | |
| 140 | + | bmp.TransparentColor := clFuchsia; | |
| 141 | + | ||
| 142 | + | MyCanvas.Draw(0, 0, bmp); | |
| 143 | + | ||
| 144 | + | bmp.Free; // Release allocated resource | |
| 145 | + | end; | |
| 146 | + | ``` | |
| 147 | + | ||
| 148 | + | Všimnite si pamäťové operácie robené pomocou [[doc:rtl/classes/tmemorystream.html|TMemoryStream]], ktoré sú potrebné na zaistenie správne načítania obrázku. | |
| 149 | + | ||
| 150 | + | ### Zachytenie snímky obrazovky | |
| 151 | + | ||
| 152 | + | Od Lazarus 0.9.16 môžete použiť LCL pre zachytenie snímku obrazovky platformovo nezávislým spôsobom. Nasledujúci príklad kódu to robí (pracuej na gtk2 a win32, ale nie na gtk1): | |
| 153 | + | ||
| 154 | + | ``` pascal | |
| 155 | + | uses LCLIntf, LCLType; | |
| 156 | + | ||
| 157 | + | ... | |
| 158 | + | ||
| 159 | + | var | |
| 160 | + | MyBitmap: TBitmap; | |
| 161 | + | ScreenDC: HDC; | |
| 162 | + | begin | |
| 163 | + | MyBitmap := TBitmap.Create; | |
| 164 | + | ScreenDC := GetDC(0); | |
| 165 | + | MyBitmap.LoadFromDevice(ScreenDC); | |
| 166 | + | ReleaseDC(ScreenDC); | |
| 167 | + | ||
| 168 | + | ... | |
| 169 | + | ``` | |
| 170 | + | ||
| 171 | + | ## Pohyblivá grafika - Ako sa vyhnúť blikaniu | |
| 172 | + | ||
| 173 | + | Mnoho programov kreslí svoj výstup do GUI ako 2D obrázky. Ak sa majú tieto obrázky rýchlo zmeniť, stretnete sa s problémom: rýchla zmena obrázkov často na obrazovke bliká. Tak sa stáva, že používatelia niekedy vidia celé obrázky a niekedy len ich čiastočné vykreslenie. To nastávapretože proces vykreslenia vyžaduje čas. | |
| 174 | + | ||
| 175 | + | Ale ako predísť blikaniu a získať vyššiu rýchlosť kreslenia? Sasmozrejme môžete pracovať s hardvérovou akceleráciou pomocou OpenGL, ale tento prístup je úplne ťažký pre malé programy alebo staré počítače. Tu sa zameriame na kreslenie do **TCanvas**. Ak potebujetepomôcť s OpenGL, pozrite si príklad, ktorý je v Lazarus. Môžete tiež použiť A.J. Ventersov gamepack, ktorý poskytuje dvojito buferované kresliace plátno a malý komponent. | |
| 176 | + | ||
| 177 | + | Teraz môžme preskúmať voľby, ktroé máme pre kreslenie do Canvas: | |
| 178 | + | ||
| 179 | + | - Kreslenie do TImage | |
| 180 | + | - Kreslenie počas udalosti OnPaint | |
| 181 | + | - Tvorba vlastného prvku, ktorý kreslí sám seba | |
| 182 | + | - Použitie A.J.Venterovho gamepack | |
| 183 | + | ||
| 184 | + | ### Kreslenie do TImage | |
| 185 | + | ||
| 186 | + | TImage pozostáva z dvoch častí: TGraphic, zvyčajne TBitmap, uchováva trvalý obrázok a viditeľnú oblasť, ktorá je prekreslená pri každom OnPaint. Zmena veľkosti TImage **nemení** veľkosť bitmapy. | |
| 187 | + | ||
| 188 | + | Obrázok (alebo bitmapa) je dostupná cez Image1.Picture.Graphic (alebo Image1.Picture.Bitmap). Plátno je Image1.Picture.Bitmap.Canvas. Plátno viditeľnej oblasti TImage je prístupné len počas Image1.OnPaint cez Image1.Canvas. | |
| 189 | + | ||
| 190 | + | ||
| 191 | + | **Dôležité**: Nikdy nepoužívajte udalosť OnPaint prvku Image1 pre kreslenie grafiky/bitmapy TImage. Obrázok z TImage je buferovaný, takže potrebujete len nakresliť ho z hcikade a zmena je tam navždy. Avšak, ak ho trvalo prekresľujete, obrázok bude blikať. V tomto prípade treba skúsiť iné vlastnosti. Kreslenie do TImage je považované za pomalšie ako iné prístupy. | |
| 192 | + | ||
| 193 | + | #### Zmena veľkosti bitmapy z TImage | |
| 194 | + | ||
| 195 | + | ||
| 196 | + | **Poznámka**: Nepoužívajte to počas OnPaint. | |
| 197 | + | ||
| 198 | + | ``` pascal | |
| 199 | + | with Image1.Picture.Bitmap do begin | |
| 200 | + | Width:=100; | |
| 201 | + | Height:=120; | |
| 202 | + | end; | |
| 203 | + | ``` | |
| 204 | + | ||
| 205 | + | #### Kreslenie na bitmapu z TImage | |
| 206 | + | ||
| 207 | + | ||
| 208 | + | **Poznámka**: Nepoužívajte to počas OnPaint. | |
| 209 | + | ||
| 210 | + | ``` pascal | |
| 211 | + | with Image1.Picture.Bitmap.Canvas do begin | |
| 212 | + | // fill the entire bitmap with red | |
| 213 | + | Brush.Color:=clRed; | |
| 214 | + | FillRect(0,0,Width,Height); | |
| 215 | + | end; | |
| 216 | + | ``` | |
| 217 | + | ||
| 218 | + | Poznámka: Vo vnútri Image1.OnPaint ukazuje Image1.Canvas na nestálu viditeľnú oblasť. Na rozdiel od Image1.OnPaint, prvok Image1.Canvas ukazuje na Image1.Picture.Bitmap.Canvas. | |
| 219 | + | ||
| 220 | + | Iný príklad: | |
| 221 | + | ||
| 222 | + | ``` pascal | |
| 223 | + | procedure TForm1.BitBtn1Click(Sender: TObject); | |
| 224 | + | var | |
| 225 | + | x, y: Integer; | |
| 226 | + | begin | |
| 227 | + | // Draws the backgroung | |
| 228 | + | MyImage.Canvas.Pen.Color := clWhite; | |
| 229 | + | MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height); | |
| 230 | + | ||
| 231 | + | // Draws squares | |
| 232 | + | MyImage.Canvas.Pen.Color := clBlack; | |
| 233 | + | for x := 1 to 8 do | |
| 234 | + | for y := 1 to 8 do | |
| 235 | + | MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8), | |
| 236 | + | Round(x * Image.Width / 8), Round(y * Image.Height / 8)); | |
| 237 | + | end; | |
| 238 | + | ``` | |
| 239 | + | ||
| 240 | + | #### Kreslenie na premenlivú vizuálnu oblasť TImage | |
| 241 | + | ||
| 242 | + | Do tejto oblasti možno kresli len počas OnPaint. OnPaint je volaná automaticky z LCL, keď sa oblasť stane neplatnou. Manuálne môžete zneplatniť oblasť pomocou Image1.Invalidate, ale toto nezavolá OnPaint okamžite a môžete ho volať koľkokrát chcete. | |
| 243 | + | ||
| 244 | + | ``` pascal | |
| 245 | + | procedure TForm.Image1Paint(Sender: TObject); | |
| 246 | + | begin | |
| 247 | + | with Image1.Canvas do begin | |
| 248 | + | // paint a line | |
| 249 | + | Pen.Color:=clRed; | |
| 250 | + | Line(0,0,Width,Height); | |
| 251 | + | end; | |
| 252 | + | end; | |
| 253 | + | ``` | |
| 254 | + | ||
| 255 | + | ### Kreslenie počas udalosti OnPaint | |
| 256 | + | ||
| 257 | + | V tomto prípade bude celé kreslenie urobené počas udalosti OnPaint formulára. Nezostáva v buferi, ako pri TImage. | |
| 258 | + | ||
| 259 | + | ### Tvorba vlastného prvku, ktorý kreslí sám seba | |
| 260 | + | ||
| 261 | + | Vytvorenie vlastného ovládacieho prvku má výhodu v štruktúrovaní svojho kódu a v opakvanom použití prvku. Toto riešenie je veľmi rýchle, ale stále môže generovať blikanie, ak nenakreslíte najprv do TBitmap a až potom nevykreslíte do plátna. v tomto prípade nie je potrebné použitie udalosti OnPaint prvku. | |
| 262 | + | ||
| 263 | + | Príklad vlastného prvku: | |
| 264 | + | ||
| 265 | + | ``` pascal | |
| 266 | + | uses | |
| 267 | + | Classes, SysUtils, Controls, Graphics, LCLType; | |
| 268 | + | ||
| 269 | + | type | |
| 270 | + | TMyDrawingControl = class(TCustomControl) | |
| 271 | + | public | |
| 272 | + | procedure EraseBackground(DC: HDC); override; | |
| 273 | + | procedure Paint; override; | |
| 274 | + | end; | |
| 275 | + | ||
| 276 | + | implementation | |
| 277 | + | ||
| 278 | + | procedure TMyDrawingControl.EraseBackground(DC: HDC); | |
| 279 | + | begin | |
| 280 | + | // Uncomment this to enable default background erasing | |
| 281 | + | //inherited EraseBackground(DC); | |
| 282 | + | end; | |
| 283 | + | ||
| 284 | + | procedure TMyDrawingControl.Paint; | |
| 285 | + | var | |
| 286 | + | x, y: Integer; | |
| 287 | + | Bitmap: TBitmap; | |
| 288 | + | begin | |
| 289 | + | Bitmap := TBitmap.Create; | |
| 290 | + | try | |
| 291 | + | // Initializes the Bitmap Size | |
| 292 | + | Bitmap.Height := Height; | |
| 293 | + | Bitmap.Width := Width; | |
| 294 | + | ||
| 295 | + | // Draws the background | |
| 296 | + | Bitmap.Canvas.Pen.Color := clWhite; | |
| 297 | + | Bitmap.Canvas.Rectangle(0, 0, Width, Height); | |
| 298 | + | ||
| 299 | + | // Draws squares | |
| 300 | + | Bitmap.Canvas.Pen.Color := clBlack; | |
| 301 | + | for x := 1 to 8 do | |
| 302 | + | for y := 1 to 8 do | |
| 303 | + | Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8), | |
| 304 | + | Round(x * Width / 8), Round(y * Height / 8)); | |
| 305 | + | ||
| 306 | + | Canvas.Draw(0, 0, Bitmap); | |
| 307 | + | finally | |
| 308 | + | Bitmap.Free; | |
| 309 | + | end; | |
| 310 | + | ||
| 311 | + | inherited Paint; | |
| 312 | + | end; | |
| 313 | + | ``` | |
| 314 | + | ||
| 315 | + | a ako ho vytvoríme na formulári: | |
| 316 | + | ||
| 317 | + | ``` pascal | |
| 318 | + | procedure TMyForm.FormCreate(Sender: TObject); | |
| 319 | + | begin | |
| 320 | + | MyDrawingControl:= TMyDrawingControl.Create(Self); | |
| 321 | + | MyDrawingControl.Height := 400; | |
| 322 | + | MyDrawingControl.Width := 500; | |
| 323 | + | MyDrawingControl.Top := 0; | |
| 324 | + | MyDrawingControl.Left := 0; | |
| 325 | + | MyDrawingControl.Parent := Self; | |
| 326 | + | MyDrawingControl.DoubleBuffered := True; | |
| 327 | + | end; | |
| 328 | + | ``` | |
| 329 | + | ||
| 330 | + | len ho nezabudnite zničiť: | |
| 331 | + | ||
| 332 | + | ``` pascal | |
| 333 | + | procedure TMyForm.FormDestroy(Sender: TObject); | |
| 334 | + | begin | |
| 335 | + | MyDrawingControl.Free; | |
| 336 | + | end; | |
| 337 | + | ``` | |
| 338 | + | ||
| 339 | + | nastvenie vlastností Top a Left na nulu nie je potrebné, pretože to je štandardná pozícia, ale je urobené aby zdôraznenie umiestneniaprvku. | |
| 340 | + | ||
| 341 | + | "MyDrawingControl.Parent := Self;" je veľmi dôležité a neuvidíte svoj prvok ak to nenastavíte. | |
| 342 | + | ||
| 343 | + | "MyDrawingControl.DoubleBuffered := True;" je vyžadované na predídenie blikania vo Windows. V gtk nemá vplyv. | |
| 344 | + | ||
| 345 | + | ### Použitie A.J.Venterovho gamepack | |
| 346 | + | ||
| 347 | + | Prístup v gamepack je kreslenie všetkého do dvojito buferovaného plátna, ktoré len berie aktualizácie viditeľného plátna, keď sme pripravení. To zaberá kúsok kódu, ale má to výhodu v schopnosti veľkej rýchlosti zmien scien s viacnásobnými škriatkami na ňom. Ak chcete použiť toto riešenie, asi Vás bude zaujímať A.J. Venterov gamepack, skupina komponentov pre vývoj hier v Lazarus, ktoré poskytujú dvojito buferovanú zobrazovaciu oblasť komponentu ako aj vnorené komponenty, navrhnuté pre dobré integrovanie s inými. gamepack môžete získať pomocou SVN: | |
| 348 | + | ||
| 349 | + | `svn co `[`svn://silentcoder.co.za/lazarus/gamepack`](svn://silentcoder.co.za/lazarus/gamepack) | |
| 350 | + | ||
| 351 | + | ## Spolupracovníci a zmeny | |
| 352 | + | ||
| 353 | + | - Preložil --Komunista 22:01, 26 December 2007 (CET) | |
| 354 | + | ||
| 355 | + | --- | |
| 356 | + | *From the Free Pascal wiki, page "Developing with Graphics/sk", as it stood on 2020-02-13 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
