Sign in Join
Wiki copytemporary

BGRABitmap tutorial 5: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11This tutorial shows you how to use layers and masks.
22
33The first part shows how to do without a multi-layer image.
44
55The second part of the tutorial shows how to adapt the code to use *TBGRALayeredBitmap*, which is located in the *BGRALayers* unit.
66
77## Without using a multi-layer image
88
99### Create a new project
1010
1111Create a new project and add a reference to [[BGRABitmap]], the same way as in [[BGRABitmap tutorial|the first tutorial]].
1212
1313### About masks
1414
1515A mask is a grayscale image. When a mask is applied on an image, the parts of the image that overlap with black parts of the mask are erased and become transparent, whereas parts that overlap with white parts of the mask are kept. In other words, the mask is like an alpha channel the defines opacity. If the mask value is zero, then it becomes transparent, and if a mask value is 255, it becomes opaque.
1616
17-<figure>
18-<img src="BGRATutorial5d.png" title="BGRATutorial5d.png" />
19-<figcaption>BGRATutorial5d.png</figcaption>
20-</figure>
17+![](/wiki-images/0/05/BGRATutorial5d.png)
2118
2219In this example, the image, the start image is in the upper-left corner, the mask in the upper-right one, and the result when applying the mask in the lower-left corner.
2320
2421Code in OnPaint:
2522
2623``` pascal
2724var temp,tex,mask: TBGRABitmap;
2825begin
2926 temp:= TBGRABitmap.Create(640,480,ColorToBGRA(ColorToRGB(clBtnFace)));
3027
3128 //loading and scaling texture
3229 tex := TBGRABitmap.Create('texture.png');
3330 BGRAReplace(tex,tex.Resample(128,80));
3431
3532 //show image in the upper-left corner
3633 temp.PutImage(10,10,tex,dmDrawWithTransparency);
3734
3835 //create a mask with ellipse and rectangle
3936 mask := TBGRABitmap.Create(128,80,BGRABlack);
4037 mask.FillEllipseAntialias(40,40,30,30,BGRAWhite);
4138 mask.FillRectAntialias(60,40,100,70,BGRAWhite);
4239
4340 //show mask in the upper-right corner
4441 temp.PutImage(150,10,mask,dmDrawWithTransparency);
4542
4643 //apply the mask to the image
4744 tex.ApplyMask(mask);
4845
4946 //show the result image in the lower-left corner
5047 temp.PutImage(10,100,tex,dmDrawWithTransparency);
5148
5249 mask.Free;
5350 tex.Free;
5451
5552 //show everything on the screen
5653 temp.Draw(Canvas,0,0,True);
5754 temp.Free;
5855end;
5956```
6057
6158### Erasing parts of an image
6259
6360Some functions allow to erase an ellipse, a rectangle etc. It means that the part of the image becomes transparent. It is thus possible to draw a hole in an image. If the alpha parameter is 255, the hole is completely transparent. If not, the hole is half-transparent.
6461
65-<figure>
66-<img src="BGRATutorial5e.png" title="BGRATutorial5e.png" />
67-<figcaption>BGRATutorial5e.png</figcaption>
68-</figure>
62+![](/wiki-images/6/62/BGRATutorial5e.png)
6963
7064Here an ellipse is erased on the left with an alpha parameter of 255, and another ellipse is erased on the right with an alpha parameter of 128.
7165
7266Code in OnPaint:
7367
7468``` pascal
7569var image,tex: TBGRABitmap;
7670begin
7771 image := TBGRABitmap.Create(640,480,ColorToBGRA(ColorToRGB(clBtnFace)));
7872
7973 //load and scale texture
8074 tex := TBGRABitmap.Create('texture.png');
8175 BGRAReplace(tex,tex.Resample(128,80));
8276
8377 //show image
8478 image.PutImage(10,10,tex,dmDrawWithTransparency);
8579
8680 //erase parts
8781 tex.EraseEllipseAntialias(40,40,30,30,255);
8882 tex.EraseEllipseAntialias(80,40,30,30,128);
8983
9084 //show result
9185 image.PutImage(10,100,tex,dmDrawWithTransparency);
9286
9387 tex.Free;
9488
9589 //show everything on the screen
9690 image.Draw(Canvas,0,0,True);
9791 image.Free;
9892end;
9993```
10094
10195### Add a painting handler
10296
10397With the object inspector, add an OnPaint handler and write:
10498
10599``` pascal
106100procedure TForm1.FormPaint(Sender: TObject);
107101var image: TBGRABitmap;
108102 size: single;
109103
110104 procedure DrawMoon;
111105 var layer: TBGRABitmap;
112106 begin
113107 layer := TBGRABitmap.Create(image.Width,image.Height);
114108 layer.FillEllipseAntialias(layer.Width/2,layer.Height/2,size*0.4,size*0.4,BGRA(224,224,224,128));
115109 layer.EraseEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.3,size*0.3,255);
116110 image.PutImage(0,0,layer,dmDrawWithTransparency);
117111 layer.Free;
118112 end;
119113
120114begin
121115 image := TBGRABitmap.Create(ClientWidth,ClientHeight);
122116
123117 //Compute available space in both directions
124118 if image.Height < image.Width then
125119 size := image.Height
126120 else
127121 size := image.Width;
128122
129123 image.GradientFill(0,0,image.Width,image.Height,
130124 BGRA(128,192,255),BGRA(0,0,255),
131125 gtLinear,PointF(0,0),PointF(0,image.Height),
132126 dmSet);
133127
134128 DrawMoon;
135129
136130 image.Draw(Canvas,0,0,True);
137131 image.free;
138132end;
139133```
140134
141135The procedure creates an image and fills it with a blue gradient. This is the background layer.
142136
143137The procedure DrawMoon creates a layer, draws a moon in it. First a white disk is drawn, then a smaller disk is subtracted. Finally, this layer is merged with the background.
144138
145139### Run the program
146140
147141You should see a blue sky with a moon. When you resize the form, the image is resized accordingly.
148142
149-<figure>
150-<img src="Tutorial5a.png" title="Tutorial5a.png" />
151-<figcaption>Tutorial5a.png</figcaption>
152-</figure>
143+![](/wiki-images/1/15/Tutorial5a.png)
153144
154145### Add another layer with a sun
155146
156147In the OnPaint event, add the following subprocedure:
157148
158149``` pascal
159150 procedure DrawSun;
160151 var layer,mask: TBGRABitmap;
161152 begin
162153 layer := TBGRABitmap.Create(image.Width,image.Height);
163154 layer.GradientFill(0,0,layer.Width,layer.Height,
164155 BGRA(255,255,0),BGRA(255,0,0),
165156 gtRadial,PointF(layer.Width/2,layer.Height/2-size*0.15),PointF(layer.Width/2+size*0.45,layer.Height/2-size*0.15),
166157 dmSet);
167158 mask := TBGRABitmap.Create(layer.Width,layer.Height,BGRABlack);
168159 mask.FillEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.25,size*0.25,BGRAWhite);
169160 layer.ApplyMask(mask);
170161 mask.Free;
171162 image.PutImage(0,0,layer,dmDrawWithTransparency);
172163 layer.Free;
173164 end;
174165```
175166
176167This procedures creates a radial gradient of red and orange and applies a circular mask to it. This results in a colored disk. Finally, the layer is merged with the background.
177168
178169Add a call to this procedure to draw it after the moon.
179170
180171### Run the program
181172
182173You should see a blue sky with a moon and a sun. When you resize the form, the image is resized accordingly.
183174
184-<figure>
185-<img src="BGRATutorial5b.png" title="BGRATutorial5b.png" />
186-<figcaption>BGRATutorial5b.png</figcaption>
187-</figure>
175+![](/wiki-images/9/9b/BGRATutorial5b.png)
188176
189177### Add a light layer
190178
191179Add the following subprocedure in the OnPaint event:
192180
193181``` pascal
194182 procedure ApplyLight;
195183 var layer: TBGRABitmap;
196184 begin
197185 layer := TBGRABitmap.Create(image.Width,image.Height);
198186 layer.GradientFill(0,0,layer.Width,layer.Height,
199187 BGRA(255,255,255),BGRA(64,64,64),
200188 gtRadial,PointF(layer.Width*5/6,layer.Height/2),PointF(layer.Width*1/3,layer.Height/4),
201189 dmSet);
202190 image.BlendImage(0,0,layer,boMultiply);
203191 layer.Free;
204192 end;
205193```
206194
207195This procedure draws a layer with a white radial gradient. It is then applied to multiply the image.
208196
209197### Resulting code
210198
211199``` pascal
212200procedure TForm1.FormPaint(Sender: TObject);
213201var image: TBGRABitmap;
214202 size: single;
215203
216204 procedure DrawMoon;
217205 var layer: TBGRABitmap;
218206 begin
219207 layer := TBGRABitmap.Create(image.Width,image.Height);
220208 layer.FillEllipseAntialias(layer.Width/2,layer.Height/2,size*0.4,size*0.4,BGRA(224,224,224,128));
221209 layer.EraseEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.3,size*0.3,255);
222210 image.PutImage(0,0,layer,dmDrawWithTransparency);
223211 layer.Free;
224212 end;
225213
226214 procedure DrawSun;
227215 var layer,mask: TBGRABitmap;
228216 begin
229217 layer := TBGRABitmap.Create(image.Width,image.Height);
230218 layer.GradientFill(0,0,layer.Width,layer.Height,
231219 BGRA(255,255,0),BGRA(255,0,0),
232220 gtRadial,PointF(layer.Width/2,layer.Height/2-size*0.15),PointF(layer.Width/2+size*0.45,layer.Height/2-size*0.15),
233221 dmSet);
234222 mask := TBGRABitmap.Create(layer.Width,layer.Height,BGRABlack);
235223 mask.FillEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.25,size*0.25,BGRAWhite);
236224 layer.ApplyMask(mask);
237225 mask.Free;
238226 image.PutImage(0,0,layer,dmDrawWithTransparency);
239227 layer.Free;
240228 end;
241229
242230 procedure ApplyLight;
243231 var layer: TBGRABitmap;
244232 begin
245233 layer := TBGRABitmap.Create(image.Width,image.Height);
246234 layer.GradientFill(0,0,layer.Width,layer.Height,
247235 BGRA(255,255,255),BGRA(64,64,64),
248236 gtRadial,PointF(layer.Width*5/6,layer.Height/2),PointF(layer.Width*1/3,layer.Height/4),
249237 dmSet);
250238 image.BlendImage(0,0,layer,boMultiply);
251239 layer.Free;
252240 end;
253241
254242begin
255243 image := TBGRABitmap.Create(ClientWidth,ClientHeight);
256244
257245 if image.Height < image.Width then
258246 size := image.Height
259247 else
260248 size := image.Width;
261249
262250 image.GradientFill(0,0,image.Width,image.Height,
263251 BGRA(128,192,255),BGRA(0,0,255),
264252 gtLinear,PointF(0,0),PointF(0,image.Height),
265253 dmSet);
266254
267255 DrawMoon;
268256 DrawSun;
269257 ApplyLight;
270258
271259 image.Draw(Canvas,0,0,True);
272260 image.free;
273261end;
274262```
275263
276264### Run the program
277265
278266You should see a blue sky with a moon and a sun, with a light effect. When you resize the form, the image is resized accordingly.
279267
280-<figure>
281-<img src="Tutorial5c.png" title="Tutorial5c.png" />
282-<figcaption>Tutorial5c.png</figcaption>
283-</figure>
268+![](/wiki-images/f/f3/Tutorial5c.png)
284269
285270## Using multi-layer images
286271
287272The "BGRALayers" unit provides a class "TBGRALayeredBitmap" which allows storing a multilayer image. You will use it to save your drawing in OpenRaster format.
288273
289274### Some adjustments to make
290275
291276First, the result will be stored not in a "TBGRABitmap" but in a "TBGRALayeredBitmap":
292277
293278``` pascal
294279var image: TBGRABitmap;
295280
296281...
297282
298283begin
299284 image := TBGRABitmap.Create(ClientWidth,ClientHeight);
300285
301286...
302287
303288 image.Draw(Canvas,0,0); // there is no Opaque parameter
304289 image.free;
305290end;
306291```
307292
308293Then, the procedures that add elements should not draw directly on the image, but add layers. Thus, the following code:
309294
310295``` pascal
311296 image.PutImage(0,0,layer,dmDrawWithTransparency);
312297 layer.Free;
313298```
314299
315300becomes:
316301
317302``` pascal
318303 image.AddOwnedLayer(layer);
319304```
320305
321306And for lighting, the following code:
322307
323308``` pascal
324309 image.BlendImage(0,0,layer,boMultiply);
325310 layer.Free;
326311```
327312
328313becomes:
329314
330315``` pascal
331316 image.AddOwnedLayer(layer,boMultiply);
332317```
333318
334319The background must now also be a layer. Add a new procedure then:
335320
336321``` pascal
337322 procedure CreateBackground;
338323 var layer: TBGRABitmap;
339324 begin
340325 layer := TBGRABitmap.Create(image.Width,image.Height);
341326 layer.GradientFill(0,0,image.Width,image.Height,
342327 BGRA(128,192,255),BGRA(0,0,255),
343328 gtLinear,PointF(0,0),PointF(0,image.Height),
344329 dmSet);
345330 image.AddOwnedLayer(layer);
346331 end;
347332```
348333
349334### Resulting Code with TBGRALayeredBitmap
350335
351336By moving the creation of the multilayer image into a separate function, "CreateMyImage", we get:
352337
353338``` pascal
354339function CreateMyImage(AWidth,AHeight: integer): TBGRALayeredBitmap;
355340var image: TBGRALayeredBitmap;
356341 size: single;
357342
358343 procedure CreateMoon;
359344 var layer: TBGRABitmap;
360345 begin
361346 layer := TBGRABitmap.Create(image.Width,image.Height);
362347 layer.FillEllipseAntialias(layer.Width/2,layer.Height/2,size*0.4,size*0.4,BGRA(224,224,224,128));
363348 layer.EraseEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.3,size*0.3,255);
364349 image.AddOwnedLayer(layer);
365350 end;
366351
367352 procedure CreateSun;
368353 var layer,mask: TBGRABitmap;
369354 begin
370355 layer := TBGRABitmap.Create(image.Width,image.Height);
371356 layer.GradientFill(0,0,layer.Width,layer.Height,
372357 BGRA(255,255,0),BGRA(255,0,0),
373358 gtRadial,PointF(layer.Width/2,layer.Height/2-size*0.15),PointF(layer.Width/2+size*0.45,layer.Height/2-size*0.15),
374359 dmSet);
375360 mask := TBGRABitmap.Create(layer.Width,layer.Height,BGRABlack);
376361 mask.FillEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.25,size*0.25,BGRAWhite);
377362 layer.ApplyMask(mask);
378363 mask.Free;
379364 image.AddOwnedLayer(layer);
380365 end;
381366
382367 procedure CreateLight;
383368 var layer: TBGRABitmap;
384369 begin
385370 layer := TBGRABitmap.Create(image.Width,image.Height);
386371 layer.GradientFill(0,0,layer.Width,layer.Height,
387372 BGRA(255,255,255),BGRA(64,64,64),
388373 gtRadial,PointF(layer.Width*5/6,layer.Height/2),PointF(layer.Width*1/3,layer.Height/4),
389374 dmSet);
390375 image.AddOwnedLayer(layer,boMultiply);
391376 end;
392377
393378 procedure CreateBackground;
394379 var layer: TBGRABitmap;
395380 begin
396381 layer := TBGRABitmap.Create(image.Width,image.Height);
397382 layer.GradientFill(0,0,image.Width,image.Height,
398383 BGRA(128,192,255),BGRA(0,0,255),
399384 gtLinear,PointF(0,0),PointF(0,image.Height),
400385 dmSet);
401386 image.AddOwnedLayer(layer);
402387 end;
403388
404389begin
405390 image := TBGRALayeredBitmap.Create(AWidth,AHeight);
406391
407392 if image.Height < image.Width then
408393 size := image.Height
409394 else
410395 size := image.Width;
411396
412397 CreateBackground;
413398 CreateMoon;
414399 CreateSun;
415400 CreateLight;
416401
417402 result := image;
418403end;
419404
420405{ TForm1 }
421406
422407procedure TForm1.FormPaint(Sender: TObject);
423408var image: TBGRALayeredBitmap;
424409begin
425410 image := CreateMyImage(ClientWidth,ClientHeight);
426411 image.Draw(Canvas,0,0);
427412 image.free;
428413end;
429414```
430415
431416### Saving the image in an OpenRaster file
432417
433418Now, let's create a button with the following code:
434419
435420``` pascal
436421procedure TForm1.Button1Click(Sender: TObject);
437422var image: TBGRALayeredBitmap;
438423begin
439424 image := CreateMyImage(ClientWidth,ClientHeight);
440425 image.SaveToFile('myimage.ora');
441426 image.free;
442427end;
443428```
444429
445430This code is fairly straightforward: it creates an image of the window size and saves it in OpenRaster format (.ora). You can then import this image into Krita, Gimp, or Paint.NET.
446431
447432[[BGRABitmap tutorial 4|Previous tutorial (direct pixel access)]] [[BGRABitmap tutorial 6|Next tutorial (line style)]]
448433
449434---
450435*From the Free Pascal wiki, page "BGRABitmap tutorial 5", as it stood on 2024-02-15 after a change by Circular, 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).*
451436Category: BGRABitmap
452437Category: Graphics

Back to history