Commit | Line | Data |
---|---|---|
ee064fb9 | 1 | #define IMAGER_NO_CONTEXT |
92bda632 TC |
2 | #include "imager.h" |
3 | #include "imageri.h" | |
f1ac5027 TC |
4 | |
5 | /* | |
773bc121 | 6 | =head1 NAME |
f1ac5027 | 7 | |
773bc121 | 8 | fills.c - implements the basic general fills |
f1ac5027 | 9 | |
773bc121 TC |
10 | =head1 SYNOPSIS |
11 | ||
12 | i_fill_t *fill; | |
13 | i_color c1, c2; | |
14 | i_fcolor fc1, fc2; | |
15 | int combine; | |
16 | fill = i_new_fill_solidf(&fc1, combine); | |
17 | fill = i_new_fill_solid(&c1, combine); | |
18 | fill = i_new_fill_hatchf(&fc1, &fc2, combine, hatch, cust_hash, dx, dy); | |
19 | fill = i_new_fill_hatch(&c1, &c2, combine, hatch, cust_hash, dx, dy); | |
f576ce7e | 20 | fill = i_new_fill_image(im, matrix, xoff, yoff, combine); |
52f2b10a | 21 | fill = i_new_fill_opacity(fill, alpha_mult); |
773bc121 TC |
22 | i_fill_destroy(fill); |
23 | ||
24 | =head1 DESCRIPTION | |
25 | ||
26 | Implements the basic general fills, which can be used for filling some | |
27 | shapes and for flood fills. | |
28 | ||
29 | Each fill can implement up to 3 functions: | |
30 | ||
31 | =over | |
32 | ||
33 | =item fill_with_color | |
34 | ||
35 | called for fills on 8-bit images. This can be NULL in which case the | |
36 | fill_with_colorf function is called. | |
37 | ||
38 | =item fill_with_fcolor | |
39 | ||
40 | called for fills on non-8-bit images or when fill_with_color is NULL. | |
41 | ||
42 | =item destroy | |
43 | ||
44 | called by i_fill_destroy() if non-NULL, to release any extra resources | |
45 | that the fill may need. | |
46 | ||
47 | =back | |
48 | ||
49 | fill_with_color and fill_with_fcolor are basically the same function | |
50 | except that the first works with lines of i_color and the second with | |
51 | lines of i_fcolor. | |
52 | ||
53 | If the combines member if non-zero the line data is populated from the | |
54 | target image before calling fill_with_*color. | |
55 | ||
56 | fill_with_color needs to fill the I<data> parameter with the fill | |
57 | pixels. If combines is non-zero it the fill pixels should be combined | |
58 | with the existing data. | |
59 | ||
60 | The current fills are: | |
61 | ||
62 | =over | |
63 | ||
64 | =item * | |
65 | ||
66 | solid fill | |
67 | ||
68 | =item * | |
69 | ||
70 | hatched fill | |
71 | ||
72 | =item * | |
73 | ||
74 | fountain fill | |
75 | ||
76 | =back | |
77 | ||
78 | Fountain fill is implemented by L<filters.c>. | |
79 | ||
efdc2568 TC |
80 | Other fills that could be implemented include: |
81 | ||
82 | =over | |
83 | ||
84 | =item * | |
85 | ||
86 | image - an image tiled over the fill area, with an offset either | |
87 | horizontally or vertically. | |
88 | ||
89 | =item * | |
90 | ||
91 | checkerboard - combine 2 fills in a checkerboard | |
92 | ||
93 | =item * | |
94 | ||
95 | combine - combine the levels of 2 other fills based in the levels of | |
96 | an image | |
97 | ||
98 | =item * | |
99 | ||
100 | regmach - use the register machine to generate colors | |
101 | ||
102 | =back | |
103 | ||
773bc121 TC |
104 | =over |
105 | ||
106 | =cut | |
f1ac5027 TC |
107 | */ |
108 | ||
97ac0a96 | 109 | static i_color fcolor_to_color(const i_fcolor *c) { |
f1ac5027 TC |
110 | int ch; |
111 | i_color out; | |
112 | ||
113 | for (ch = 0; ch < MAXCHANNELS; ++ch) | |
114 | out.channel[ch] = SampleFTo8(c->channel[ch]); | |
976efad5 TC |
115 | |
116 | return out; | |
f1ac5027 TC |
117 | } |
118 | ||
97ac0a96 | 119 | static i_fcolor color_to_fcolor(const i_color *c) { |
f1ac5027 | 120 | int ch; |
976efad5 | 121 | i_fcolor out; |
f1ac5027 TC |
122 | |
123 | for (ch = 0; ch < MAXCHANNELS; ++ch) | |
124 | out.channel[ch] = Sample8ToF(c->channel[ch]); | |
976efad5 TC |
125 | |
126 | return out; | |
f1ac5027 TC |
127 | } |
128 | ||
efdc2568 | 129 | /* alpha combine in with out */ |
f1ac5027 TC |
130 | #define COMBINE(out, in, channels) \ |
131 | { \ | |
132 | int ch; \ | |
133 | for (ch = 0; ch < (channels); ++ch) { \ | |
134 | (out).channel[ch] = ((out).channel[ch] * (255 - (in).channel[3]) \ | |
135 | + (in).channel[ch] * (in).channel[3]) / 255; \ | |
136 | } \ | |
137 | } | |
138 | ||
efdc2568 TC |
139 | /* alpha combine in with out, in this case in is a simple array of |
140 | samples, potentially not integers - the mult combiner uses doubles | |
141 | for accuracy */ | |
142 | #define COMBINEA(out, in, channels) \ | |
143 | { \ | |
144 | int ch; \ | |
145 | for (ch = 0; ch < (channels); ++ch) { \ | |
146 | (out).channel[ch] = ((out).channel[ch] * (255 - (in)[3]) \ | |
147 | + (in)[ch] * (in)[3]) / 255; \ | |
148 | } \ | |
149 | } | |
150 | ||
f1ac5027 TC |
151 | #define COMBINEF(out, in, channels) \ |
152 | { \ | |
153 | int ch; \ | |
154 | for (ch = 0; ch < (channels); ++ch) { \ | |
155 | (out).channel[ch] = (out).channel[ch] * (1.0 - (in).channel[3]) \ | |
156 | + (in).channel[ch] * (in).channel[3]; \ | |
157 | } \ | |
158 | } | |
159 | ||
efdc2568 TC |
160 | typedef struct |
161 | { | |
162 | i_fill_t base; | |
163 | i_color c; | |
164 | i_fcolor fc; | |
165 | } i_fill_solid_t; | |
166 | ||
50c75381 TC |
167 | static void fill_solid(i_fill_t *, i_img_dim x, i_img_dim y, i_img_dim width, |
168 | int channels, i_color *); | |
169 | static void fill_solidf(i_fill_t *, i_img_dim x, i_img_dim y, i_img_dim width, | |
170 | int channels, i_fcolor *); | |
f1ac5027 TC |
171 | |
172 | static i_fill_solid_t base_solid_fill = | |
173 | { | |
174 | { | |
175 | fill_solid, | |
176 | fill_solidf, | |
177 | NULL, | |
efdc2568 TC |
178 | NULL, |
179 | NULL, | |
f1ac5027 TC |
180 | }, |
181 | }; | |
f1ac5027 | 182 | |
773bc121 TC |
183 | /* |
184 | =item i_fill_destroy(fill) | |
6cfee9d1 | 185 | =order 90 |
92bda632 | 186 | =category Fills |
9167a5c6 | 187 | =synopsis i_fill_destroy(fill); |
92bda632 | 188 | |
773bc121 TC |
189 | Call to destroy any fill object. |
190 | ||
191 | =cut | |
192 | */ | |
193 | ||
f1ac5027 TC |
194 | void |
195 | i_fill_destroy(i_fill_t *fill) { | |
196 | if (fill->destroy) | |
197 | (fill->destroy)(fill); | |
198 | myfree(fill); | |
199 | } | |
200 | ||
773bc121 TC |
201 | /* |
202 | =item i_new_fill_solidf(color, combine) | |
203 | ||
92bda632 | 204 | =category Fills |
9167a5c6 | 205 | =synopsis i_fill_t *fill = i_new_fill_solidf(&fcolor, combine); |
92bda632 | 206 | |
773bc121 TC |
207 | Create a solid fill based on a float color. |
208 | ||
209 | If combine is non-zero then alpha values will be combined. | |
210 | ||
211 | =cut | |
212 | */ | |
213 | ||
f1ac5027 | 214 | i_fill_t * |
97ac0a96 | 215 | i_new_fill_solidf(const i_fcolor *c, int combine) { |
f1ac5027 | 216 | int ch; |
f0960b14 | 217 | i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */ |
f1ac5027 | 218 | |
9b1ec2b8 | 219 | *fill = base_solid_fill; |
141a6114 | 220 | if (combine) { |
efdc2568 TC |
221 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); |
222 | } | |
9b1ec2b8 | 223 | |
f1ac5027 TC |
224 | fill->fc = *c; |
225 | for (ch = 0; ch < MAXCHANNELS; ++ch) { | |
226 | fill->c.channel[ch] = SampleFTo8(c->channel[ch]); | |
227 | } | |
228 | ||
229 | return &fill->base; | |
230 | } | |
231 | ||
773bc121 TC |
232 | /* |
233 | =item i_new_fill_solid(color, combine) | |
234 | ||
92bda632 | 235 | =category Fills |
9167a5c6 | 236 | =synopsis i_fill_t *fill = i_new_fill_solid(&color, combine); |
92bda632 TC |
237 | |
238 | Create a solid fill based on an 8-bit color. | |
773bc121 TC |
239 | |
240 | If combine is non-zero then alpha values will be combined. | |
241 | ||
242 | =cut | |
243 | */ | |
244 | ||
f1ac5027 | 245 | i_fill_t * |
97ac0a96 | 246 | i_new_fill_solid(const i_color *c, int combine) { |
f1ac5027 | 247 | int ch; |
f0960b14 | 248 | i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */ |
f1ac5027 | 249 | |
9b1ec2b8 | 250 | *fill = base_solid_fill; |
141a6114 | 251 | if (combine) { |
efdc2568 TC |
252 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); |
253 | } | |
9b1ec2b8 | 254 | |
f1ac5027 TC |
255 | fill->c = *c; |
256 | for (ch = 0; ch < MAXCHANNELS; ++ch) { | |
257 | fill->fc.channel[ch] = Sample8ToF(c->channel[ch]); | |
258 | } | |
259 | ||
260 | return &fill->base; | |
261 | } | |
262 | ||
f1ac5027 TC |
263 | static unsigned char |
264 | builtin_hatches[][8] = | |
265 | { | |
266 | { | |
267 | /* 1x1 checkerboard */ | |
268 | 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, | |
269 | }, | |
270 | { | |
271 | /* 2x2 checkerboard */ | |
272 | 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, | |
273 | }, | |
274 | { | |
275 | /* 4 x 4 checkerboard */ | |
276 | 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, | |
277 | }, | |
278 | { | |
279 | /* single vertical lines */ | |
280 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, | |
281 | }, | |
282 | { | |
283 | /* double vertical lines */ | |
284 | 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, | |
285 | }, | |
286 | { | |
287 | /* quad vertical lines */ | |
288 | 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, | |
289 | }, | |
290 | { | |
291 | /* single hlines */ | |
292 | 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
293 | }, | |
294 | { | |
295 | /* double hlines */ | |
296 | 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, | |
297 | }, | |
298 | { | |
299 | /* quad hlines */ | |
300 | 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, | |
301 | }, | |
302 | { | |
303 | /* single / */ | |
304 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, | |
305 | }, | |
306 | { | |
307 | /* single \ */ | |
308 | 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, | |
309 | }, | |
310 | { | |
311 | /* double / */ | |
312 | 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, | |
313 | }, | |
314 | { | |
315 | /* double \ */ | |
316 | 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11, | |
317 | }, | |
318 | { | |
319 | /* single grid */ | |
320 | 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, | |
321 | }, | |
322 | { | |
323 | /* double grid */ | |
324 | 0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88, | |
325 | }, | |
326 | { | |
327 | /* quad grid */ | |
328 | 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, | |
329 | }, | |
330 | { | |
331 | /* single dots */ | |
332 | 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
333 | }, | |
334 | { | |
335 | /* 4 dots */ | |
336 | 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, | |
337 | }, | |
338 | { | |
339 | /* 16 dots */ | |
340 | 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, | |
341 | }, | |
342 | { | |
343 | /* simple stipple */ | |
344 | 0x48, 0x84, 0x00, 0x00, 0x84, 0x48, 0x00, 0x00, | |
345 | }, | |
346 | { | |
347 | /* weave */ | |
348 | 0x55, 0xFD, 0x05, 0xFD, 0x55, 0xDF, 0x50, 0xDF, | |
349 | }, | |
350 | { | |
351 | /* single cross hatch */ | |
352 | 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x01, | |
353 | }, | |
354 | { | |
355 | /* double cross hatch */ | |
356 | 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11, | |
357 | }, | |
358 | { | |
359 | /* vertical lozenge */ | |
360 | 0x11, 0x11, 0x11, 0xAA, 0x44, 0x44, 0x44, 0xAA, | |
361 | }, | |
362 | { | |
363 | /* horizontal lozenge */ | |
364 | 0x88, 0x70, 0x88, 0x07, 0x88, 0x70, 0x88, 0x07, | |
365 | }, | |
366 | { | |
367 | /* scales overlapping downwards */ | |
7a606d29 | 368 | 0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3, |
f1ac5027 TC |
369 | }, |
370 | { | |
371 | /* scales overlapping upwards */ | |
7a606d29 | 372 | 0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01, |
f1ac5027 TC |
373 | }, |
374 | { | |
375 | /* scales overlapping leftwards */ | |
7a606d29 | 376 | 0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84, |
f1ac5027 TC |
377 | }, |
378 | { | |
379 | /* scales overlapping rightwards */ | |
7a606d29 | 380 | 0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1, |
f1ac5027 TC |
381 | }, |
382 | { | |
383 | /* denser stipple */ | |
384 | 0x44, 0x88, 0x22, 0x11, 0x44, 0x88, 0x22, 0x11, | |
385 | }, | |
386 | { | |
387 | /* L-shaped tiles */ | |
388 | 0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90, | |
389 | }, | |
cc6483e0 TC |
390 | { |
391 | /* wider stipple */ | |
392 | 0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00, | |
393 | }, | |
f1ac5027 TC |
394 | }; |
395 | ||
396 | typedef struct | |
397 | { | |
398 | i_fill_t base; | |
399 | i_color fg, bg; | |
400 | i_fcolor ffg, fbg; | |
401 | unsigned char hatch[8]; | |
8d14daab | 402 | i_img_dim dx, dy; |
f1ac5027 TC |
403 | } i_fill_hatch_t; |
404 | ||
50c75381 TC |
405 | static void fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y, |
406 | i_img_dim width, int channels, i_color *data); | |
407 | static void fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y, | |
408 | i_img_dim width, int channels, i_fcolor *data); | |
773bc121 TC |
409 | static |
410 | i_fill_t * | |
97ac0a96 TC |
411 | i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const i_fcolor *fbg, |
412 | int combine, int hatch, const unsigned char *cust_hatch, | |
8d14daab | 413 | i_img_dim dx, i_img_dim dy); |
773bc121 TC |
414 | |
415 | /* | |
5715f7c3 | 416 | =item i_new_fill_hatch(C<fg>, C<bg>, C<combine>, C<hatch>, C<cust_hatch>, C<dx>, C<dy>) |
773bc121 | 417 | |
92bda632 | 418 | =category Fills |
9167a5c6 | 419 | =synopsis i_fill_t *fill = i_new_fill_hatch(&fg_color, &bg_color, combine, hatch, custom_hatch, dx, dy); |
92bda632 | 420 | |
5715f7c3 TC |
421 | Creates a new hatched fill with the C<fg> color used for the 1 bits in |
422 | the hatch and C<bg> for the 0 bits. If C<combine> is non-zero alpha | |
423 | values will be combined. | |
f1ac5027 | 424 | |
5715f7c3 | 425 | If C<cust_hatch> is non-NULL it should be a pointer to 8 bytes of the |
773bc121 TC |
426 | hash definition, with the high-bits to the left. |
427 | ||
5715f7c3 | 428 | If C<cust_hatch> is NULL then one of the standard hatches is used. |
773bc121 | 429 | |
5715f7c3 TC |
430 | (C<dx>, C<dy>) are an offset into the hatch which can be used to hatch |
431 | adjoining areas out of alignment, or to align the origin of a hatch | |
3a01f799 | 432 | with the side of a filled area. |
773bc121 TC |
433 | |
434 | =cut | |
435 | */ | |
436 | i_fill_t * | |
97ac0a96 | 437 | i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch, |
8d14daab | 438 | const unsigned char *cust_hatch, i_img_dim dx, i_img_dim dy) { |
773bc121 TC |
439 | return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, |
440 | dx, dy); | |
441 | } | |
442 | ||
443 | /* | |
5715f7c3 | 444 | =item i_new_fill_hatchf(C<fg>, C<bg>, C<combine>, C<hatch>, C<cust_hatch>, C<dx>, C<dy>) |
773bc121 | 445 | |
92bda632 | 446 | =category Fills |
9167a5c6 | 447 | =synopsis i_fill_t *fill = i_new_fill_hatchf(&fg_fcolor, &bg_fcolor, combine, hatch, custom_hatch, dx, dy); |
92bda632 | 448 | |
5715f7c3 TC |
449 | Creates a new hatched fill with the C<fg> color used for the 1 bits in |
450 | the hatch and C<bg> for the 0 bits. If C<combine> is non-zero alpha | |
451 | values will be combined. | |
773bc121 | 452 | |
5715f7c3 | 453 | If C<cust_hatch> is non-NULL it should be a pointer to 8 bytes of the |
773bc121 TC |
454 | hash definition, with the high-bits to the left. |
455 | ||
5715f7c3 | 456 | If C<cust_hatch> is NULL then one of the standard hatches is used. |
773bc121 | 457 | |
5715f7c3 TC |
458 | (C<dx>, C<dy>) are an offset into the hatch which can be used to hatch |
459 | adjoining areas out of alignment, or to align the origin of a hatch | |
3a01f799 | 460 | with the side of a filled area. |
773bc121 TC |
461 | |
462 | =cut | |
463 | */ | |
464 | i_fill_t * | |
97ac0a96 | 465 | i_new_fill_hatchf(const i_fcolor *fg, const i_fcolor *bg, int combine, int hatch, |
8d14daab | 466 | const unsigned char *cust_hatch, i_img_dim dx, i_img_dim dy) { |
773bc121 TC |
467 | return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, |
468 | dx, dy); | |
469 | } | |
470 | ||
50c75381 TC |
471 | static void fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y, |
472 | i_img_dim width, int channels, i_color *data); | |
473 | static void fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y, | |
474 | i_img_dim width, int channels, i_fcolor *data); | |
f576ce7e TC |
475 | struct i_fill_image_t { |
476 | i_fill_t base; | |
477 | i_img *src; | |
8d14daab | 478 | i_img_dim xoff, yoff; |
f576ce7e TC |
479 | int has_matrix; |
480 | double matrix[9]; | |
481 | }; | |
482 | ||
9b1ec2b8 TC |
483 | static struct i_fill_image_t |
484 | image_fill_proto = | |
485 | { | |
486 | { | |
487 | fill_image, | |
488 | fill_imagef, | |
489 | NULL | |
490 | } | |
491 | }; | |
492 | ||
f576ce7e | 493 | /* |
5715f7c3 | 494 | =item i_new_fill_image(C<im>, C<matrix>, C<xoff>, C<yoff>, C<combine>) |
f576ce7e | 495 | |
92bda632 | 496 | =category Fills |
9167a5c6 | 497 | =synopsis i_fill_t *fill = i_new_fill_image(src_img, matrix, x_offset, y_offset, combine); |
92bda632 | 498 | |
f576ce7e TC |
499 | Create an image based fill. |
500 | ||
92bda632 TC |
501 | matrix is an array of 9 doubles representing a transformation matrix. |
502 | ||
5715f7c3 | 503 | C<xoff> and C<yoff> are the offset into the image to start filling from. |
92bda632 | 504 | |
f576ce7e TC |
505 | =cut |
506 | */ | |
507 | i_fill_t * | |
8d14daab | 508 | i_new_fill_image(i_img *im, const double *matrix, i_img_dim xoff, i_img_dim yoff, int combine) { |
f0960b14 | 509 | struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */ |
f576ce7e | 510 | |
9b1ec2b8 | 511 | *fill = image_fill_proto; |
f576ce7e TC |
512 | |
513 | if (combine) { | |
514 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); | |
515 | } | |
516 | else { | |
517 | fill->base.combine = NULL; | |
518 | fill->base.combinef = NULL; | |
519 | } | |
520 | fill->src = im; | |
521 | if (xoff < 0) | |
522 | xoff += im->xsize; | |
523 | fill->xoff = xoff; | |
524 | if (yoff < 0) | |
525 | yoff += im->ysize; | |
526 | fill->yoff = yoff; | |
527 | if (matrix) { | |
528 | fill->has_matrix = 1; | |
529 | memcpy(fill->matrix, matrix, sizeof(fill->matrix)); | |
530 | } | |
531 | else | |
532 | fill->has_matrix = 0; | |
533 | ||
534 | return &fill->base; | |
535 | } | |
536 | ||
50c75381 TC |
537 | static void fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y, |
538 | i_img_dim width, int channels, i_color *data); | |
539 | static void fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y, | |
540 | i_img_dim width, int channels, i_fcolor *data); | |
52f2b10a TC |
541 | |
542 | struct i_fill_opacity_t { | |
543 | i_fill_t base; | |
544 | i_fill_t *other_fill; | |
545 | double alpha_mult; | |
546 | }; | |
547 | ||
548 | static struct i_fill_opacity_t | |
549 | opacity_fill_proto = | |
550 | { | |
8d14daab TC |
551 | { |
552 | fill_opacity, | |
553 | fill_opacityf, | |
554 | NULL | |
555 | } | |
52f2b10a TC |
556 | }; |
557 | ||
558 | i_fill_t * | |
559 | i_new_fill_opacity(i_fill_t *base_fill, double alpha_mult) { | |
560 | struct i_fill_opacity_t *fill = mymalloc(sizeof(*fill)); | |
561 | *fill = opacity_fill_proto; | |
562 | ||
563 | fill->base.combine = base_fill->combine; | |
564 | fill->base.combinef = base_fill->combinef; | |
565 | ||
566 | fill->other_fill = base_fill; | |
567 | fill->alpha_mult = alpha_mult; | |
568 | ||
8c194049 TC |
569 | if (!base_fill->f_fill_with_color) { |
570 | /* base fill only does floating, so we only do that too */ | |
571 | fill->base.f_fill_with_color = NULL; | |
572 | } | |
573 | ||
52f2b10a TC |
574 | return &fill->base; |
575 | } | |
f576ce7e | 576 | |
773bc121 TC |
577 | #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill)) |
578 | ||
579 | /* | |
580 | =back | |
581 | ||
582 | =head1 INTERNAL FUNCTIONS | |
583 | ||
584 | =over | |
585 | ||
586 | =item fill_solid(fill, x, y, width, channels, data) | |
587 | ||
588 | The 8-bit sample fill function for non-combining solid fills. | |
589 | ||
590 | =cut | |
591 | */ | |
592 | static void | |
50c75381 TC |
593 | fill_solid(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, |
594 | int channels, i_color *data) { | |
9b1ec2b8 TC |
595 | i_color c = T_SOLID_FILL(fill)->c; |
596 | i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1); | |
773bc121 | 597 | while (width-- > 0) { |
9b1ec2b8 | 598 | *data++ = c; |
773bc121 TC |
599 | } |
600 | } | |
601 | ||
602 | /* | |
603 | =item fill_solid(fill, x, y, width, channels, data) | |
604 | ||
605 | The floating sample fill function for non-combining solid fills. | |
606 | ||
607 | =cut | |
608 | */ | |
609 | static void | |
50c75381 TC |
610 | fill_solidf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, |
611 | int channels, i_fcolor *data) { | |
773bc121 | 612 | i_fcolor c = T_SOLID_FILL(fill)->fc; |
9b1ec2b8 | 613 | i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1); |
773bc121 | 614 | while (width-- > 0) { |
43c5dacb | 615 | *data++ = c; |
773bc121 TC |
616 | } |
617 | } | |
618 | ||
9b1ec2b8 TC |
619 | static i_fill_hatch_t |
620 | hatch_fill_proto = | |
621 | { | |
622 | { | |
623 | fill_hatch, | |
624 | fill_hatchf, | |
625 | NULL | |
626 | } | |
627 | }; | |
628 | ||
773bc121 TC |
629 | /* |
630 | =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy) | |
631 | ||
632 | Implements creation of hatch fill objects. | |
633 | ||
634 | =cut | |
635 | */ | |
f1ac5027 TC |
636 | static |
637 | i_fill_t * | |
97ac0a96 TC |
638 | i_new_hatch_low(const i_color *fg, const i_color *bg, |
639 | const i_fcolor *ffg, const i_fcolor *fbg, | |
640 | int combine, int hatch, const unsigned char *cust_hatch, | |
8d14daab | 641 | i_img_dim dx, i_img_dim dy) { |
f0960b14 | 642 | i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */ |
f1ac5027 | 643 | |
9b1ec2b8 | 644 | *fill = hatch_fill_proto; |
05462f4b TC |
645 | /* Some Sun C didn't like the condition expressions that were here. |
646 | See https://rt.cpan.org/Ticket/Display.html?id=21944 | |
647 | */ | |
648 | if (fg) | |
649 | fill->fg = *fg; | |
650 | else | |
651 | fill->fg = fcolor_to_color(ffg); | |
652 | if (bg) | |
653 | fill->bg = *bg; | |
654 | else | |
655 | fill->bg = fcolor_to_color(fbg); | |
656 | if (ffg) | |
657 | fill->ffg = *ffg; | |
658 | else | |
659 | fill->ffg = color_to_fcolor(fg); | |
660 | if (fbg) | |
661 | fill->fbg = *fbg; | |
662 | else | |
663 | fill->fbg = color_to_fcolor(bg); | |
141a6114 | 664 | if (combine) { |
efdc2568 TC |
665 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); |
666 | } | |
667 | else { | |
668 | fill->base.combine = NULL; | |
669 | fill->base.combinef = NULL; | |
670 | } | |
f1ac5027 TC |
671 | if (cust_hatch) { |
672 | memcpy(fill->hatch, cust_hatch, 8); | |
673 | } | |
674 | else { | |
675 | if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) | |
676 | hatch = 0; | |
677 | memcpy(fill->hatch, builtin_hatches[hatch], 8); | |
678 | } | |
679 | fill->dx = dx & 7; | |
680 | fill->dy = dy & 7; | |
681 | ||
682 | return &fill->base; | |
683 | } | |
684 | ||
773bc121 TC |
685 | /* |
686 | =item fill_hatch(fill, x, y, width, channels, data) | |
f1ac5027 | 687 | |
773bc121 | 688 | The 8-bit sample fill function for hatched fills. |
f1ac5027 | 689 | |
b8c2033e | 690 | =cut |
773bc121 | 691 | */ |
50c75381 TC |
692 | static void |
693 | fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, | |
694 | int channels, i_color *data) { | |
f1ac5027 TC |
695 | i_fill_hatch_t *f = (i_fill_hatch_t *)fill; |
696 | int byte = f->hatch[(y + f->dy) & 7]; | |
697 | int xpos = (x + f->dx) & 7; | |
698 | int mask = 128 >> xpos; | |
04f85f63 TC |
699 | i_color fg = f->fg; |
700 | i_color bg = f->bg; | |
04f85f63 TC |
701 | |
702 | if (channels < 3) { | |
703 | i_adapt_colors(2, 4, &fg, 1); | |
704 | i_adapt_colors(2, 4, &bg, 1); | |
705 | } | |
f1ac5027 | 706 | |
43c5dacb | 707 | while (width-- > 0) { |
052acec4 | 708 | if (byte & mask) |
04f85f63 | 709 | *data++ = fg; |
052acec4 | 710 | else |
04f85f63 | 711 | *data++ = bg; |
43c5dacb TC |
712 | |
713 | if ((mask >>= 1) == 0) | |
714 | mask = 128; | |
f1ac5027 TC |
715 | } |
716 | } | |
717 | ||
773bc121 TC |
718 | /* |
719 | =item fill_hatchf(fill, x, y, width, channels, data) | |
720 | ||
721 | The floating sample fill function for hatched fills. | |
722 | ||
12db268a | 723 | =cut |
773bc121 | 724 | */ |
50c75381 TC |
725 | static void |
726 | fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, | |
727 | int channels, i_fcolor *data) { | |
f1ac5027 TC |
728 | i_fill_hatch_t *f = (i_fill_hatch_t *)fill; |
729 | int byte = f->hatch[(y + f->dy) & 7]; | |
730 | int xpos = (x + f->dx) & 7; | |
731 | int mask = 128 >> xpos; | |
04f85f63 TC |
732 | i_fcolor fg = f->ffg; |
733 | i_fcolor bg = f->fbg; | |
734 | ||
735 | if (channels < 3) { | |
736 | i_adapt_fcolors(2, 4, &fg, 1); | |
737 | i_adapt_fcolors(2, 4, &bg, 1); | |
738 | } | |
f1ac5027 | 739 | |
43c5dacb | 740 | while (width-- > 0) { |
052acec4 | 741 | if (byte & mask) |
04f85f63 | 742 | *data++ = fg; |
052acec4 | 743 | else |
04f85f63 | 744 | *data++ = bg; |
43c5dacb TC |
745 | |
746 | if ((mask >>= 1) == 0) | |
747 | mask = 128; | |
efdc2568 TC |
748 | } |
749 | } | |
750 | ||
f576ce7e TC |
751 | /* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ |
752 | /* linear interpolation */ | |
753 | static i_color interp_i_color(i_color before, i_color after, double pos, | |
754 | int channels) { | |
755 | i_color out; | |
756 | int ch; | |
757 | ||
758 | pos -= floor(pos); | |
759 | for (ch = 0; ch < channels; ++ch) | |
760 | out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; | |
3efb0915 | 761 | if (channels > 3 && out.channel[3]) |
f576ce7e TC |
762 | for (ch = 0; ch < channels; ++ch) |
763 | if (ch != 3) { | |
764 | int temp = out.channel[ch] * 255 / out.channel[3]; | |
765 | if (temp > 255) | |
766 | temp = 255; | |
767 | out.channel[ch] = temp; | |
768 | } | |
769 | ||
770 | return out; | |
771 | } | |
772 | ||
773 | /* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */ | |
774 | /* linear interpolation */ | |
775 | static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos, | |
776 | int channels) { | |
777 | i_fcolor out; | |
778 | int ch; | |
779 | ||
780 | pos -= floor(pos); | |
781 | for (ch = 0; ch < channels; ++ch) | |
782 | out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch]; | |
783 | if (out.channel[3]) | |
784 | for (ch = 0; ch < channels; ++ch) | |
785 | if (ch != 3) { | |
786 | int temp = out.channel[ch] / out.channel[3]; | |
787 | if (temp > 1.0) | |
788 | temp = 1.0; | |
789 | out.channel[ch] = temp; | |
790 | } | |
791 | ||
792 | return out; | |
793 | } | |
794 | ||
795 | /* | |
796 | =item fill_image(fill, x, y, width, channels, data, work) | |
797 | ||
798 | =cut | |
799 | */ | |
50c75381 TC |
800 | static void |
801 | fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, | |
802 | int channels, i_color *data) { | |
f576ce7e | 803 | struct i_fill_image_t *f = (struct i_fill_image_t *)fill; |
50c75381 | 804 | i_img_dim i = 0; |
cde2dbc7 | 805 | i_color *out = data; |
a256aec5 | 806 | int want_channels = channels > 2 ? 4 : 2; |
f576ce7e TC |
807 | |
808 | if (f->has_matrix) { | |
809 | /* the hard way */ | |
810 | while (i < width) { | |
811 | double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2]; | |
812 | double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5]; | |
813 | double ix = floor(rx / f->src->xsize); | |
814 | double iy = floor(ry / f->src->ysize); | |
815 | i_color c[2][2]; | |
816 | i_color c2[2]; | |
50c75381 | 817 | i_img_dim dy; |
f576ce7e TC |
818 | |
819 | if (f->xoff) { | |
820 | rx += iy * f->xoff; | |
821 | ix = floor(rx / f->src->xsize); | |
822 | } | |
823 | else if (f->yoff) { | |
824 | ry += ix * f->yoff; | |
825 | iy = floor(ry / f->src->ysize); | |
826 | } | |
827 | rx -= ix * f->src->xsize; | |
828 | ry -= iy * f->src->ysize; | |
829 | ||
830 | for (dy = 0; dy < 2; ++dy) { | |
8d14daab TC |
831 | if ((i_img_dim)rx == f->src->xsize-1) { |
832 | i_gpix(f->src, f->src->xsize-1, ((i_img_dim)ry+dy) % f->src->ysize, &c[dy][0]); | |
833 | i_gpix(f->src, 0, ((i_img_dim)ry+dy) % f->src->xsize, &c[dy][1]); | |
f576ce7e TC |
834 | } |
835 | else { | |
8d14daab | 836 | i_glin(f->src, (i_img_dim)rx, (i_img_dim)rx+2, ((i_img_dim)ry+dy) % f->src->ysize, |
f576ce7e TC |
837 | c[dy]); |
838 | } | |
839 | c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels); | |
840 | } | |
cde2dbc7 | 841 | *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels); |
f576ce7e TC |
842 | ++i; |
843 | } | |
844 | } | |
845 | else { | |
846 | /* the easy way */ | |
847 | /* this should be possible to optimize to use i_glin() */ | |
848 | while (i < width) { | |
50c75381 TC |
849 | i_img_dim rx = x+i; |
850 | i_img_dim ry = y; | |
851 | i_img_dim ix = rx / f->src->xsize; | |
852 | i_img_dim iy = ry / f->src->ysize; | |
f576ce7e TC |
853 | |
854 | if (f->xoff) { | |
855 | rx += iy * f->xoff; | |
856 | ix = rx / f->src->xsize; | |
857 | } | |
858 | else if (f->yoff) { | |
859 | ry += ix * f->yoff; | |
73236585 | 860 | iy = ry / f->src->ysize; |
f576ce7e TC |
861 | } |
862 | rx -= ix * f->src->xsize; | |
863 | ry -= iy * f->src->ysize; | |
cde2dbc7 TC |
864 | i_gpix(f->src, rx, ry, out); |
865 | ++out; | |
f576ce7e TC |
866 | ++i; |
867 | } | |
868 | } | |
a256aec5 TC |
869 | if (f->src->channels != want_channels) |
870 | i_adapt_colors(want_channels, f->src->channels, data, width); | |
f576ce7e TC |
871 | } |
872 | ||
873 | /* | |
a256aec5 | 874 | =item fill_imagef(fill, x, y, width, channels, data, work) |
f576ce7e TC |
875 | |
876 | =cut | |
877 | */ | |
50c75381 TC |
878 | static void |
879 | fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, | |
880 | int channels, i_fcolor *data) { | |
f576ce7e | 881 | struct i_fill_image_t *f = (struct i_fill_image_t *)fill; |
50c75381 | 882 | i_img_dim i = 0; |
a256aec5 | 883 | int want_channels = channels > 2 ? 4 : 2; |
f576ce7e TC |
884 | |
885 | if (f->has_matrix) { | |
a256aec5 | 886 | i_fcolor *work_data = data; |
f576ce7e TC |
887 | /* the hard way */ |
888 | while (i < width) { | |
889 | double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2]; | |
890 | double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5]; | |
891 | double ix = floor(rx / f->src->xsize); | |
892 | double iy = floor(ry / f->src->ysize); | |
893 | i_fcolor c[2][2]; | |
894 | i_fcolor c2[2]; | |
50c75381 | 895 | i_img_dim dy; |
f576ce7e TC |
896 | |
897 | if (f->xoff) { | |
898 | rx += iy * f->xoff; | |
899 | ix = floor(rx / f->src->xsize); | |
900 | } | |
901 | else if (f->yoff) { | |
902 | ry += ix * f->yoff; | |
903 | iy = floor(ry / f->src->ysize); | |
904 | } | |
905 | rx -= ix * f->src->xsize; | |
906 | ry -= iy * f->src->ysize; | |
907 | ||
908 | for (dy = 0; dy < 2; ++dy) { | |
8d14daab TC |
909 | if ((i_img_dim)rx == f->src->xsize-1) { |
910 | i_gpixf(f->src, f->src->xsize-1, ((i_img_dim)ry+dy) % f->src->ysize, &c[dy][0]); | |
911 | i_gpixf(f->src, 0, ((i_img_dim)ry+dy) % f->src->xsize, &c[dy][1]); | |
f576ce7e TC |
912 | } |
913 | else { | |
8d14daab | 914 | i_glinf(f->src, (i_img_dim)rx, (i_img_dim)rx+2, ((i_img_dim)ry+dy) % f->src->ysize, |
f576ce7e TC |
915 | c[dy]); |
916 | } | |
917 | c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels); | |
918 | } | |
a256aec5 | 919 | *work_data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels); |
f576ce7e TC |
920 | ++i; |
921 | } | |
922 | } | |
923 | else { | |
a256aec5 | 924 | i_fcolor *work_data = data; |
f576ce7e TC |
925 | /* the easy way */ |
926 | /* this should be possible to optimize to use i_glin() */ | |
927 | while (i < width) { | |
50c75381 TC |
928 | i_img_dim rx = x+i; |
929 | i_img_dim ry = y; | |
930 | i_img_dim ix = rx / f->src->xsize; | |
931 | i_img_dim iy = ry / f->src->ysize; | |
f576ce7e TC |
932 | |
933 | if (f->xoff) { | |
934 | rx += iy * f->xoff; | |
935 | ix = rx / f->src->xsize; | |
936 | } | |
937 | else if (f->yoff) { | |
938 | ry += ix * f->yoff; | |
939 | iy = ry / f->src->xsize; | |
940 | } | |
941 | rx -= ix * f->src->xsize; | |
942 | ry -= iy * f->src->ysize; | |
a256aec5 TC |
943 | i_gpixf(f->src, rx, ry, work_data); |
944 | ++work_data; | |
f576ce7e TC |
945 | ++i; |
946 | } | |
947 | } | |
a256aec5 TC |
948 | if (f->src->channels != want_channels) |
949 | i_adapt_fcolors(want_channels, f->src->channels, data, width); | |
f576ce7e TC |
950 | } |
951 | ||
52f2b10a | 952 | static void |
50c75381 TC |
953 | fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, |
954 | int channels, i_color *data) { | |
52f2b10a | 955 | struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill; |
e958b64e | 956 | int alpha_chan = channels > 2 ? 3 : 1; |
52f2b10a TC |
957 | i_color *datap = data; |
958 | ||
959 | (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data); | |
960 | while (width--) { | |
961 | double new_alpha = datap->channel[alpha_chan] * f->alpha_mult; | |
962 | if (new_alpha < 0) | |
963 | datap->channel[alpha_chan] = 0; | |
964 | else if (new_alpha > 255) | |
965 | datap->channel[alpha_chan] = 255; | |
966 | else datap->channel[alpha_chan] = (int)(new_alpha + 0.5); | |
967 | ||
968 | ++datap; | |
969 | } | |
970 | } | |
971 | static void | |
50c75381 TC |
972 | fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, |
973 | int channels, i_fcolor *data) { | |
d0eb6658 | 974 | struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill; |
e958b64e | 975 | int alpha_chan = channels > 2 ? 3 : 1; |
52f2b10a TC |
976 | i_fcolor *datap = data; |
977 | ||
978 | (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data); | |
979 | ||
980 | while (width--) { | |
981 | double new_alpha = datap->channel[alpha_chan] * f->alpha_mult; | |
982 | if (new_alpha < 0) | |
983 | datap->channel[alpha_chan] = 0; | |
984 | else if (new_alpha > 1.0) | |
985 | datap->channel[alpha_chan] = 1.0; | |
986 | else datap->channel[alpha_chan] = new_alpha; | |
987 | ||
988 | ++datap; | |
989 | } | |
990 | } | |
efdc2568 | 991 | |
773bc121 TC |
992 | /* |
993 | =back | |
994 | ||
995 | =head1 AUTHOR | |
996 | ||
997 | Tony Cook <tony@develop-help.com> | |
998 | ||
999 | =head1 SEE ALSO | |
1000 | ||
1001 | Imager(3) | |
1002 | ||
1003 | =cut | |
1004 | */ |