Sign in Join
Wiki copytemporary

BGRABitmap tutorial 5: what changed

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

Title: BGRABitmap tutorial 5

OldNewLine
1+This tutorial shows you how to use layers and masks.
2+
3+The first part shows how to do without a multi-layer image.
4+
5+The second part of the tutorial shows how to adapt the code to use *TBGRALayeredBitmap*, which is located in the *BGRALayers* unit.
6+
7+## Without using a multi-layer image
8+
9+### Create a new project
10+
11+Create a new project and add a reference to [[BGRABitmap]], the same way as in [[BGRABitmap tutorial|the first tutorial]].
12+
13+### About masks
14+
15+A 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.
16+
17+<figure>
18+<img src="BGRATutorial5d.png" title="BGRATutorial5d.png" />
19+<figcaption>BGRATutorial5d.png</figcaption>
20+</figure>
21+
22+In 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.
23+
24+Code in OnPaint:
25+
26+``` pascal
27+var temp,tex,mask: TBGRABitmap;
28+begin
29+ temp:= TBGRABitmap.Create(640,480,ColorToBGRA(ColorToRGB(clBtnFace)));
30+
31+ //loading and scaling texture
32+ tex := TBGRABitmap.Create('texture.png');
33+ BGRAReplace(tex,tex.Resample(128,80));
34+
35+ //show image in the upper-left corner
36+ temp.PutImage(10,10,tex,dmDrawWithTransparency);
37+
38+ //create a mask with ellipse and rectangle
39+ mask := TBGRABitmap.Create(128,80,BGRABlack);
40+ mask.FillEllipseAntialias(40,40,30,30,BGRAWhite);
41+ mask.FillRectAntialias(60,40,100,70,BGRAWhite);
42+
43+ //show mask in the upper-right corner
44+ temp.PutImage(150,10,mask,dmDrawWithTransparency);
45+
46+ //apply the mask to the image
47+ tex.ApplyMask(mask);
48+
49+ //show the result image in the lower-left corner
50+ temp.PutImage(10,100,tex,dmDrawWithTransparency);
51+
52+ mask.Free;
53+ tex.Free;
54+
55+ //show everything on the screen
56+ temp.Draw(Canvas,0,0,True);
57+ temp.Free;
58+end;
59+```
60+
61+### Erasing parts of an image
62+
63+Some 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.
64+
65+<figure>
66+<img src="BGRATutorial5e.png" title="BGRATutorial5e.png" />
67+<figcaption>BGRATutorial5e.png</figcaption>
68+</figure>
69+
70+Here 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.
71+
72+Code in OnPaint:
73+
74+``` pascal
75+var image,tex: TBGRABitmap;
76+begin
77+ image := TBGRABitmap.Create(640,480,ColorToBGRA(ColorToRGB(clBtnFace)));
78+
79+ //load and scale texture
80+ tex := TBGRABitmap.Create('texture.png');
81+ BGRAReplace(tex,tex.Resample(128,80));
82+
83+ //show image
84+ image.PutImage(10,10,tex,dmDrawWithTransparency);
85+
86+ //erase parts
87+ tex.EraseEllipseAntialias(40,40,30,30,255);
88+ tex.EraseEllipseAntialias(80,40,30,30,128);
89+
90+ //show result
91+ image.PutImage(10,100,tex,dmDrawWithTransparency);
92+
93+ tex.Free;
94+
95+ //show everything on the screen
96+ image.Draw(Canvas,0,0,True);
97+ image.Free;
98+end;
99+```
100+
101+### Add a painting handler
102+
103+With the object inspector, add an OnPaint handler and write:
104+
105+``` pascal
106+procedure TForm1.FormPaint(Sender: TObject);
107+var image: TBGRABitmap;
108+ size: single;
109+
110+ procedure DrawMoon;
111+ var layer: TBGRABitmap;
112+ begin
113+ layer := TBGRABitmap.Create(image.Width,image.Height);
114+ layer.FillEllipseAntialias(layer.Width/2,layer.Height/2,size*0.4,size*0.4,BGRA(224,224,224,128));
115+ layer.EraseEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.3,size*0.3,255);
116+ image.PutImage(0,0,layer,dmDrawWithTransparency);
117+ layer.Free;
118+ end;
119+
120+begin
121+ image := TBGRABitmap.Create(ClientWidth,ClientHeight);
122+
123+ //Compute available space in both directions
124+ if image.Height < image.Width then
125+ size := image.Height
126+ else
127+ size := image.Width;
128+
129+ image.GradientFill(0,0,image.Width,image.Height,
130+ BGRA(128,192,255),BGRA(0,0,255),
131+ gtLinear,PointF(0,0),PointF(0,image.Height),
132+ dmSet);
133+
134+ DrawMoon;
135+
136+ image.Draw(Canvas,0,0,True);
137+ image.free;
138+end;
139+```
140+
141+The procedure creates an image and fills it with a blue gradient. This is the background layer.
142+
143+The 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.
144+
145+### Run the program
146+
147+You should see a blue sky with a moon. When you resize the form, the image is resized accordingly.
148+
149+<figure>
150+<img src="Tutorial5a.png" title="Tutorial5a.png" />
151+<figcaption>Tutorial5a.png</figcaption>
152+</figure>
153+
154+### Add another layer with a sun
155+
156+In the OnPaint event, add the following subprocedure:
157+
158+``` pascal
159+ procedure DrawSun;
160+ var layer,mask: TBGRABitmap;
161+ begin
162+ layer := TBGRABitmap.Create(image.Width,image.Height);
163+ layer.GradientFill(0,0,layer.Width,layer.Height,
164+ BGRA(255,255,0),BGRA(255,0,0),
165+ gtRadial,PointF(layer.Width/2,layer.Height/2-size*0.15),PointF(layer.Width/2+size*0.45,layer.Height/2-size*0.15),
166+ dmSet);
167+ mask := TBGRABitmap.Create(layer.Width,layer.Height,BGRABlack);
168+ mask.FillEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.25,size*0.25,BGRAWhite);
169+ layer.ApplyMask(mask);
170+ mask.Free;
171+ image.PutImage(0,0,layer,dmDrawWithTransparency);
172+ layer.Free;
173+ end;
174+```
175+
176+This 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.
177+
178+Add a call to this procedure to draw it after the moon.
179+
180+### Run the program
181+
182+You should see a blue sky with a moon and a sun. When you resize the form, the image is resized accordingly.
183+
184+<figure>
185+<img src="BGRATutorial5b.png" title="BGRATutorial5b.png" />
186+<figcaption>BGRATutorial5b.png</figcaption>
187+</figure>
188+
189+### Add a light layer
190+
191+Add the following subprocedure in the OnPaint event:
192+
193+``` pascal
194+ procedure ApplyLight;
195+ var layer: TBGRABitmap;
196+ begin
197+ layer := TBGRABitmap.Create(image.Width,image.Height);
198+ layer.GradientFill(0,0,layer.Width,layer.Height,
199+ BGRA(255,255,255),BGRA(64,64,64),
200+ gtRadial,PointF(layer.Width*5/6,layer.Height/2),PointF(layer.Width*1/3,layer.Height/4),
201+ dmSet);
202+ image.BlendImage(0,0,layer,boMultiply);
203+ layer.Free;
204+ end;
205+```
206+
207+This procedure draws a layer with a white radial gradient. It is then applied to multiply the image.
208+
209+### Resulting code
210+
211+``` pascal
212+procedure TForm1.FormPaint(Sender: TObject);
213+var image: TBGRABitmap;
214+ size: single;
215+
216+ procedure DrawMoon;
217+ var layer: TBGRABitmap;
218+ begin
219+ layer := TBGRABitmap.Create(image.Width,image.Height);
220+ layer.FillEllipseAntialias(layer.Width/2,layer.Height/2,size*0.4,size*0.4,BGRA(224,224,224,128));
221+ layer.EraseEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.3,size*0.3,255);
222+ image.PutImage(0,0,layer,dmDrawWithTransparency);
223+ layer.Free;
224+ end;
225+
226+ procedure DrawSun;
227+ var layer,mask: TBGRABitmap;
228+ begin
229+ layer := TBGRABitmap.Create(image.Width,image.Height);
230+ layer.GradientFill(0,0,layer.Width,layer.Height,
231+ BGRA(255,255,0),BGRA(255,0,0),
232+ gtRadial,PointF(layer.Width/2,layer.Height/2-size*0.15),PointF(layer.Width/2+size*0.45,layer.Height/2-size*0.15),
233+ dmSet);
234+ mask := TBGRABitmap.Create(layer.Width,layer.Height,BGRABlack);
235+ mask.FillEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.25,size*0.25,BGRAWhite);
236+ layer.ApplyMask(mask);
237+ mask.Free;
238+ image.PutImage(0,0,layer,dmDrawWithTransparency);
239+ layer.Free;
240+ end;
241+
242+ procedure ApplyLight;
243+ var layer: TBGRABitmap;
244+ begin
245+ layer := TBGRABitmap.Create(image.Width,image.Height);
246+ layer.GradientFill(0,0,layer.Width,layer.Height,
247+ BGRA(255,255,255),BGRA(64,64,64),
248+ gtRadial,PointF(layer.Width*5/6,layer.Height/2),PointF(layer.Width*1/3,layer.Height/4),
249+ dmSet);
250+ image.BlendImage(0,0,layer,boMultiply);
251+ layer.Free;
252+ end;
253+
254+begin
255+ image := TBGRABitmap.Create(ClientWidth,ClientHeight);
256+
257+ if image.Height < image.Width then
258+ size := image.Height
259+ else
260+ size := image.Width;
261+
262+ image.GradientFill(0,0,image.Width,image.Height,
263+ BGRA(128,192,255),BGRA(0,0,255),
264+ gtLinear,PointF(0,0),PointF(0,image.Height),
265+ dmSet);
266+
267+ DrawMoon;
268+ DrawSun;
269+ ApplyLight;
270+
271+ image.Draw(Canvas,0,0,True);
272+ image.free;
273+end;
274+```
275+
276+### Run the program
277+
278+You 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.
279+
280+<figure>
281+<img src="Tutorial5c.png" title="Tutorial5c.png" />
282+<figcaption>Tutorial5c.png</figcaption>
283+</figure>
284+
285+## Using multi-layer images
286+
287+The "BGRALayers" unit provides a class "TBGRALayeredBitmap" which allows storing a multilayer image. You will use it to save your drawing in OpenRaster format.
288+
289+### Some adjustments to make
290+
291+First, the result will be stored not in a "TBGRABitmap" but in a "TBGRALayeredBitmap":
292+
293+``` pascal
294+var image: TBGRABitmap;
295+
296+...
297+
298+begin
299+ image := TBGRABitmap.Create(ClientWidth,ClientHeight);
300+
301+...
302+
303+ image.Draw(Canvas,0,0); // there is no Opaque parameter
304+ image.free;
305+end;
306+```
307+
308+Then, the procedures that add elements should not draw directly on the image, but add layers. Thus, the following code:
309+
310+``` pascal
311+ image.PutImage(0,0,layer,dmDrawWithTransparency);
312+ layer.Free;
313+```
314+
315+becomes:
316+
317+``` pascal
318+ image.AddOwnedLayer(layer);
319+```
320+
321+And for lighting, the following code:
322+
323+``` pascal
324+ image.BlendImage(0,0,layer,boMultiply);
325+ layer.Free;
326+```
327+
328+becomes:
329+
330+``` pascal
331+ image.AddOwnedLayer(layer,boMultiply);
332+```
333+
334+The background must now also be a layer. Add a new procedure then:
335+
336+``` pascal
337+ procedure CreateBackground;
338+ var layer: TBGRABitmap;
339+ begin
340+ layer := TBGRABitmap.Create(image.Width,image.Height);
341+ layer.GradientFill(0,0,image.Width,image.Height,
342+ BGRA(128,192,255),BGRA(0,0,255),
343+ gtLinear,PointF(0,0),PointF(0,image.Height),
344+ dmSet);
345+ image.AddOwnedLayer(layer);
346+ end;
347+```
348+
349+### Resulting Code with TBGRALayeredBitmap
350+
351+By moving the creation of the multilayer image into a separate function, "CreateMyImage", we get:
352+
353+``` pascal
354+function CreateMyImage(AWidth,AHeight: integer): TBGRALayeredBitmap;
355+var image: TBGRALayeredBitmap;
356+ size: single;
357+
358+ procedure CreateMoon;
359+ var layer: TBGRABitmap;
360+ begin
361+ layer := TBGRABitmap.Create(image.Width,image.Height);
362+ layer.FillEllipseAntialias(layer.Width/2,layer.Height/2,size*0.4,size*0.4,BGRA(224,224,224,128));
363+ layer.EraseEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.3,size*0.3,255);
364+ image.AddOwnedLayer(layer);
365+ end;
366+
367+ procedure CreateSun;
368+ var layer,mask: TBGRABitmap;
369+ begin
370+ layer := TBGRABitmap.Create(image.Width,image.Height);
371+ layer.GradientFill(0,0,layer.Width,layer.Height,
372+ BGRA(255,255,0),BGRA(255,0,0),
373+ gtRadial,PointF(layer.Width/2,layer.Height/2-size*0.15),PointF(layer.Width/2+size*0.45,layer.Height/2-size*0.15),
374+ dmSet);
375+ mask := TBGRABitmap.Create(layer.Width,layer.Height,BGRABlack);
376+ mask.FillEllipseAntialias(layer.Width/2+size*0.15,layer.Height/2,size*0.25,size*0.25,BGRAWhite);
377+ layer.ApplyMask(mask);
378+ mask.Free;
379+ image.AddOwnedLayer(layer);
380+ end;
381+
382+ procedure CreateLight;
383+ var layer: TBGRABitmap;
384+ begin
385+ layer := TBGRABitmap.Create(image.Width,image.Height);
386+ layer.GradientFill(0,0,layer.Width,layer.Height,
387+ BGRA(255,255,255),BGRA(64,64,64),
388+ gtRadial,PointF(layer.Width*5/6,layer.Height/2),PointF(layer.Width*1/3,layer.Height/4),
389+ dmSet);
390+ image.AddOwnedLayer(layer,boMultiply);
391+ end;
392+
393+ procedure CreateBackground;
394+ var layer: TBGRABitmap;
395+ begin
396+ layer := TBGRABitmap.Create(image.Width,image.Height);
397+ layer.GradientFill(0,0,image.Width,image.Height,
398+ BGRA(128,192,255),BGRA(0,0,255),
399+ gtLinear,PointF(0,0),PointF(0,image.Height),
400+ dmSet);
401+ image.AddOwnedLayer(layer);
402+ end;
403+
404+begin
405+ image := TBGRALayeredBitmap.Create(AWidth,AHeight);
406+
407+ if image.Height < image.Width then
408+ size := image.Height
409+ else
410+ size := image.Width;
411+
412+ CreateBackground;
413+ CreateMoon;
414+ CreateSun;
415+ CreateLight;
416+
417+ result := image;
418+end;
419+
420+{ TForm1 }
421+
422+procedure TForm1.FormPaint(Sender: TObject);
423+var image: TBGRALayeredBitmap;
424+begin
425+ image := CreateMyImage(ClientWidth,ClientHeight);
426+ image.Draw(Canvas,0,0);
427+ image.free;
428+end;
429+```
430+
431+### Saving the image in an OpenRaster file
432+
433+Now, let's create a button with the following code:
434+
435+``` pascal
436+procedure TForm1.Button1Click(Sender: TObject);
437+var image: TBGRALayeredBitmap;
438+begin
439+ image := CreateMyImage(ClientWidth,ClientHeight);
440+ image.SaveToFile('myimage.ora');
441+ image.free;
442+end;
443+```
444+
445+This 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.
446+
447+[[BGRABitmap tutorial 4|Previous tutorial (direct pixel access)]] [[BGRABitmap tutorial 6|Next tutorial (line style)]]
448+
449+---
450+*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).*
451+Category: BGRABitmap
452+Category: Graphics

Back to history