Sign in Join
Wiki copytemporary

BGRABitmap tutorial 8: what changed

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

OldNewLine
11This tutorial shows how to use textures.
22
33### Create a new project
44
55Create a new project and add a reference to [[BGRABitmap]], the same way as in [[BGRABitmap tutorial|the first tutorial]].
66
77### Using brush textures
88
99The simplest texture is a hatched brush.
1010
1111With the object inspector, add an OnPaint handler and write:
1212
1313``` pascal
1414procedure TForm1.FormPaint(Sender: TObject);
1515var
1616 image,tex: TBGRABitmap;
1717 c: TBGRAPixel;
1818 x,y,rx,ry: single;
1919
2020begin
2121 image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));
2222 c := ColorToBGRA(ColorToRGB(clWindowText));
2323
2424 //ellipse coordinates
2525 x := 150;
2626 y := 100;
2727 rx := 100;
2828 ry := 50;
2929
3030 //loads a "diagcross" brush with white pattern and orange background
3131 tex := image.CreateBrushTexture(bsDiagCross,BGRAWhite,BGRA(255,192,0)) as TBGRABitmap;
3232
3333 image.FillEllipseAntialias(x,y,rx-0.5,ry-0.5,tex);
3434 image.EllipseAntialias(x,y,rx,ry,c,1); //draw outline
3535
3636 tex.Free;
3737
3838 image.Draw(Canvas,0,0,True);
3939 image.free;
4040end;
4141```
4242
4343As you can see, a texture is just some bitmap. To fill an ellipse with a texture, just pass the texture as a parameter instead of the color.
4444
4545Two commands define the ellipse. The first is the filling, and the second is the outline. Notice that the radius is 0.5 pixel smaller for the filling. Indeed, when the pen width is 1, the inner radius is 0.5 smaller and the outer radius is 0.5 greater. Nevertheless, even if you pay attention to this, the result will not be perfect. If you want an antialiased junction between polygons, you need to use TBGRAMultishapeFiller in the BGRAPolygon unit and draw everything at once.
4646
4747Using a command for the outline, we achieve to draw a textured ellipse with a border. But if the outline function is not available, you can also use another fill command with a greater radius with the border color first, and then a smaller radius for the inside.
4848
4949Add the following lines before tex.Free:
5050
5151``` pascal
5252 image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,c,11);
5353 image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,tex,9);
5454```
5555
5656The first command draws a wide round rectangle (of width 11) that includes the border. The second command fill with the texture with a smaller width (9). This works perfectly as long as the texture is not transparent.
5757
5858### Run the program
5959
6060You should obtain a round rectangle with an ellipse inside it. Each shape is filled with an orange texture.
6161
62-<figure>
63-<img src="BGRATutorial8.png" title="BGRATutorial8.png" />
64-<figcaption>BGRATutorial8.png</figcaption>
65-</figure>
62+![](/wiki-images/b/b7/BGRATutorial8.png)
6663
6764### Generating textures
6865
6966#### Basic Perlin noise map
7067
7168It is possible to generate tilable random textures using CreateCyclicPerlinNoiseMap that can be found in the BGRAGradients unit.
7269
7370With the object inspector, define the OnPaint handler with:
7471
7572``` pascal
7673uses
7774 BGRABitmap, BGRABitmapTypes, BGRAGradients;
7875
7976procedure TForm1.FormPaint(Sender: TObject);
8077var
8178 image,tex: TBGRABitmap;
8279
8380begin
8481 image := TBGRABitmap.Create(ClientWidth,ClientHeight);
8582
8683 tex := CreateCyclicPerlinNoiseMap(100,100);
8784 image.FillRect(0,0,image.Width,image.Height, tex, dmSet);
8885 tex.free;
8986
9087 image.Draw(Canvas,0,0,True);
9188 image.free;
9289end;
9390```
9491
9592This creates a 100x100 texture, and fills the form with it. You should obtain something like this:
9693
97-<figure>
98-<img src="BGRATutorial8b.png" title="BGRATutorial8b.png" />
99-<figcaption>BGRATutorial8b.png</figcaption>
100-</figure>
94+![](/wiki-images/3/3e/BGRATutorial8b.png)
10195
10296#### Changing color
10397
10498This is very black and white. We can add some colors. For this, we will need some function to interpolate values. Here it is:
10599
106100``` pascal
107101 function Interp256(value1,value2,position: integer): integer; inline;
108102 begin
109103 result := (value1*(256-position) + value2*position) shr 8;
110104 end;
111105```
112106
113107This function computes a value going from value1 to value2. Position is a number between 0 and 256 which indicates how close the result is to the second value. The expression "shr 8" is an optimized equivalent to "div 256" for positive values. It is a binary shift of 8 digits.
114108
115109We want to interpolate colors, so let's write a function to interpolate colors:
116110
117111``` pascal
118112 function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline;
119113 begin
120114 result.red := Interp256(color1.red,color2.red, position);
121115 result.green := Interp256(color1.green,color2.green, position);
122116 result.blue := Interp256(color1.blue,color2.blue, position);
123117 result.alpha := Interp256(color1.alpha,color2.alpha, position);
124118 end;
125119```
126120
127121It is straightforward : each color component is interpolated between color1 and color2 values.
128122
129123Now we have everything to make some colors. After CreatePerlinNoiseMap, add the following lines:
130124
131125``` pascal
132126 p := tex.Data;
133127 for i := 0 to tex.NbPixels-1 do
134128 begin
135129 p^ := Interp256( BGRA(0,128,0), BGRA(192,255,0), p^.red );
136130 inc(p);
137131 end;
138132```
139133
140134You'll need variables 'p' and 'i', so click on each and press Ctrl-Shift-C.
141135
142136This loop takes each pixel and creates a color from dark green to light green-yellow.
143137
144138We obtain a tree-like green color:
145139
146-<figure>
147-<img src="BGRATutorial8c.png" title="BGRATutorial8c.png" />
148-<figcaption>BGRATutorial8c.png</figcaption>
149-</figure>
140+![](/wiki-images/9/93/BGRATutorial8c.png)
150141
151142#### Using thresholds
152143
153144Instead of varying continuously, the color can be changed with a threshold. For example, we can delimit sea and islands:
154145
155146``` pascal
156147 p := tex.Data;
157148 for i := 0 to tex.NbPixels-1 do
158149 begin
159150 if p^.red > 196 then
160151 p^ := BGRA(192,160,96) else //island
161152 p^ := BGRA(0,128,196); //sea
162153 inc(p);
163154 end;
164155```
165156
166157We can use more thresholds. Here is a camouflage:
167158
168159``` pascal
169160 p := result.Data;
170161 for i := 0 to result.NbPixels-1 do
171162 begin
172163 v := p^.red;
173164 if v < 64 then p^:= BGRA(31,33,46) else
174165 if v < 128 then p^:= BGRA(89,71,57) else
175166 if v < 192 then p^:= BGRA(80,106,67) else
176167 p^:= BGRA(161,157,121);
177168 inc(p);
178169 end;
179170```
180171
181-<figure>
182-<img src="BGRATutorial8d.png" title="BGRATutorial8d.png" />
183-<figcaption>BGRATutorial8d.png</figcaption>
184-</figure>
172+![](/wiki-images/d/d6/BGRATutorial8d.png)
185173
186174#### Sine function
187175
188176We will apply here a sine function to noise values. Let's create a procedure for this:
189177
190178``` pascal
191179 function CreateCustomTexture(tx,ty: integer): TBGRABitmap;
192180 var
193181 colorOscillation: integer;
194182 p: PBGRAPixel;
195183 i: Integer;
196184 begin
197185 result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1);
198186 p := result.Data;
199187 for i := 0 to result.NbPixels-1 do
200188 begin
201189 colorOscillation := round(((sin(p^.red*Pi/32)+1)/2)*256);
202190 p^ := Interp256(BGRA(181,157,105),BGRA(228,227,180),colorOscillation);
203191 inc(p);
204192 end;
205193 end;
206194```
207195
208196The color oscillation is a value between 0 and 256. It is calculated out of the intensity (p^.red). Here we apply a sine function with a half-period of 32. This gives a number between -1 and 1. To put it in the range 0..1, we add 1 and divide by 2. Finally, we multiply by 256 to have an integer for Interp256.
209197
210198The OnPaint procedure becomes simpler:
211199
212200``` pascal
213201var
214202 image,tex: TBGRABitmap;
215203
216204begin
217205 image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));
218206
219207 tex := CreateCustomTexture(100,100);
220208 image.FillRoundRectAntialias(20,20,300,200,20,20,tex);
221209 image.RoundRectAntialias(20,20,300,200,20,20,BGRABlack,1);
222210 tex.free;
223211
224212 image.Draw(Canvas,0,0,True);
225213 image.free;
226214end;
227215```
228216
229217You should obtain something like this:
230218
231-<figure>
232-<img src="BGRATutorial8e.png" title="BGRATutorial8e.png" />
233-<figcaption>BGRATutorial8e.png</figcaption>
234-</figure>
219+![](/wiki-images/8/8c/BGRATutorial8e.png)
235220
236221Now, if we want it to look like marble, we need less oscillations. For example, we can use a half-period of 80. On marble, the black parts are very thin. We can distort the oscillation by applying the power function : an exponent between 0 and 1 will make the values closer to 1 and an exponent greater than 1 will make the values closer to 0. Let's change the oscillation like this in CreateCustomTexture:
237222
238223``` pascal
239224 colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256);
240225```
241226
242227Now we have something much more like marble:
243228
244-<figure>
245-<img src="BGRATutorial8f.png" title="BGRATutorial8f.png" />
246-<figcaption>BGRATutorial8f.png</figcaption>
247-</figure>
229+![](/wiki-images/9/95/BGRATutorial8f.png)
248230
249231#### Wood texture
250232
251233Wood texture can be achieved with sine functions too. Wood texture contains two oscillations, one with light colors, and another one with dark colors. So we need to apply a global variation between these oscillations:
252234
253235``` pascal
254236
255237 function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
256238 var
257239 colorOscillation, globalColorVariation: integer;
258240 p: PBGRAPixel;
259241 i: Integer;
260242 begin
261243 result := CreateCyclicPerlinNoiseMap(tx,ty);
262244 p := result.Data;
263245 for i := 0 to result.NbPixels-1 do
264246 begin
265247 colorOscillation := round(sqrt((sin(p^.red*Pi/16)+1)/2)*256);
266248 globalColorVariation := p^.red;
267249 p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
268250 Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
269251 inc(p);
270252 end;
271253 end;
272254```
273255
274256Here the half-period is 16, and the global variation is simply the intensity. The result is something like this:
275257
276-<figure>
277-<img src="BGRATutorial8g.png" title="BGRATutorial8g.png" />
278-<figcaption>BGRATutorial8g.png</figcaption>
279-</figure>
258+![](/wiki-images/d/d0/BGRATutorial8g.png)
280259
281260Most of the time, a wood texture is oriented along an axis. To do this, instead of using intensity only as a global position, we need to combine it with the x position:
282261
283262``` pascal
284263 function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
285264 var
286265 globalPos: single;
287266 colorOscillation, globalColorVariation: integer;
288267 p: PBGRAPixel;
289268 i: Integer;
290269 x: integer;
291270 begin
292271 result := CreateCyclicPerlinNoiseMap(tx,ty);
293272 p := result.Data;
294273 x := 0;
295274 for i := 0 to result.NbPixels-1 do
296275 begin
297276 globalPos := p^.red*Pi/32+x*2*Pi/tx*8;
298277 colorOscillation := round(sqrt((sin(globalPos)+1)/2)*256);
299278 globalColorVariation := round(sin(globalPos/8)*128+128);
300279 p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
301280 Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
302281 inc(p);
303282 inc(x);
304283 if x = tx then x := 0;
305284 end;
306285 end;
307286```
308287
309288We obtain this:
310289
311-<figure>
312-<img src="Tutorial8h.png" title="Tutorial8h.png" />
313-<figcaption>Tutorial8h.png</figcaption>
314-</figure>
290+![](/wiki-images/3/37/Tutorial8h.png)
315291
316292[[BGRABitmap tutorial 7|Previous tutorial (splines)]] [[BGRABitmap tutorial 9|Next tutorial (phong shading and textures)]]
317293
318294---
319295*From the Free Pascal wiki, page "BGRABitmap tutorial 8", as it stood on 2020-01-02 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).*
320296Category: BGRABitmap
321297Category: Graphics

Back to history