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