Commit | Line | Data |
---|---|---|
f1ac5027 TC |
1 | #include "image.h" |
2 | #include "imagei.h" | |
3 | ||
4 | /* | |
773bc121 | 5 | =head1 NAME |
f1ac5027 | 6 | |
773bc121 | 7 | fills.c - implements the basic general fills |
f1ac5027 | 8 | |
773bc121 TC |
9 | =head1 SYNOPSIS |
10 | ||
11 | i_fill_t *fill; | |
12 | i_color c1, c2; | |
13 | i_fcolor fc1, fc2; | |
14 | int combine; | |
15 | fill = i_new_fill_solidf(&fc1, combine); | |
16 | fill = i_new_fill_solid(&c1, combine); | |
17 | fill = i_new_fill_hatchf(&fc1, &fc2, combine, hatch, cust_hash, dx, dy); | |
18 | fill = i_new_fill_hatch(&c1, &c2, combine, hatch, cust_hash, dx, dy); | |
19 | i_fill_destroy(fill); | |
20 | ||
21 | =head1 DESCRIPTION | |
22 | ||
23 | Implements the basic general fills, which can be used for filling some | |
24 | shapes and for flood fills. | |
25 | ||
26 | Each fill can implement up to 3 functions: | |
27 | ||
28 | =over | |
29 | ||
30 | =item fill_with_color | |
31 | ||
32 | called for fills on 8-bit images. This can be NULL in which case the | |
33 | fill_with_colorf function is called. | |
34 | ||
35 | =item fill_with_fcolor | |
36 | ||
37 | called for fills on non-8-bit images or when fill_with_color is NULL. | |
38 | ||
39 | =item destroy | |
40 | ||
41 | called by i_fill_destroy() if non-NULL, to release any extra resources | |
42 | that the fill may need. | |
43 | ||
44 | =back | |
45 | ||
46 | fill_with_color and fill_with_fcolor are basically the same function | |
47 | except that the first works with lines of i_color and the second with | |
48 | lines of i_fcolor. | |
49 | ||
50 | If the combines member if non-zero the line data is populated from the | |
51 | target image before calling fill_with_*color. | |
52 | ||
53 | fill_with_color needs to fill the I<data> parameter with the fill | |
54 | pixels. If combines is non-zero it the fill pixels should be combined | |
55 | with the existing data. | |
56 | ||
57 | The current fills are: | |
58 | ||
59 | =over | |
60 | ||
61 | =item * | |
62 | ||
63 | solid fill | |
64 | ||
65 | =item * | |
66 | ||
67 | hatched fill | |
68 | ||
69 | =item * | |
70 | ||
71 | fountain fill | |
72 | ||
73 | =back | |
74 | ||
75 | Fountain fill is implemented by L<filters.c>. | |
76 | ||
efdc2568 TC |
77 | Other fills that could be implemented include: |
78 | ||
79 | =over | |
80 | ||
81 | =item * | |
82 | ||
83 | image - an image tiled over the fill area, with an offset either | |
84 | horizontally or vertically. | |
85 | ||
86 | =item * | |
87 | ||
88 | checkerboard - combine 2 fills in a checkerboard | |
89 | ||
90 | =item * | |
91 | ||
92 | combine - combine the levels of 2 other fills based in the levels of | |
93 | an image | |
94 | ||
95 | =item * | |
96 | ||
97 | regmach - use the register machine to generate colors | |
98 | ||
99 | =back | |
100 | ||
773bc121 TC |
101 | =over |
102 | ||
103 | =cut | |
f1ac5027 TC |
104 | */ |
105 | ||
106 | static i_color fcolor_to_color(i_fcolor *c) { | |
107 | int ch; | |
108 | i_color out; | |
109 | ||
110 | for (ch = 0; ch < MAXCHANNELS; ++ch) | |
111 | out.channel[ch] = SampleFTo8(c->channel[ch]); | |
112 | } | |
113 | ||
114 | static i_fcolor color_to_fcolor(i_color *c) { | |
115 | int ch; | |
116 | i_color out; | |
117 | ||
118 | for (ch = 0; ch < MAXCHANNELS; ++ch) | |
119 | out.channel[ch] = Sample8ToF(c->channel[ch]); | |
120 | } | |
121 | ||
efdc2568 | 122 | /* alpha combine in with out */ |
f1ac5027 TC |
123 | #define COMBINE(out, in, channels) \ |
124 | { \ | |
125 | int ch; \ | |
126 | for (ch = 0; ch < (channels); ++ch) { \ | |
127 | (out).channel[ch] = ((out).channel[ch] * (255 - (in).channel[3]) \ | |
128 | + (in).channel[ch] * (in).channel[3]) / 255; \ | |
129 | } \ | |
130 | } | |
131 | ||
efdc2568 TC |
132 | /* alpha combine in with out, in this case in is a simple array of |
133 | samples, potentially not integers - the mult combiner uses doubles | |
134 | for accuracy */ | |
135 | #define COMBINEA(out, in, channels) \ | |
136 | { \ | |
137 | int ch; \ | |
138 | for (ch = 0; ch < (channels); ++ch) { \ | |
139 | (out).channel[ch] = ((out).channel[ch] * (255 - (in)[3]) \ | |
140 | + (in)[ch] * (in)[3]) / 255; \ | |
141 | } \ | |
142 | } | |
143 | ||
f1ac5027 TC |
144 | #define COMBINEF(out, in, channels) \ |
145 | { \ | |
146 | int ch; \ | |
147 | for (ch = 0; ch < (channels); ++ch) { \ | |
148 | (out).channel[ch] = (out).channel[ch] * (1.0 - (in).channel[3]) \ | |
149 | + (in).channel[ch] * (in).channel[3]; \ | |
150 | } \ | |
151 | } | |
152 | ||
efdc2568 TC |
153 | typedef struct |
154 | { | |
155 | i_fill_t base; | |
156 | i_color c; | |
157 | i_fcolor fc; | |
158 | } i_fill_solid_t; | |
159 | ||
f1ac5027 | 160 | static void fill_solid(i_fill_t *, int x, int y, int width, int channels, |
efdc2568 | 161 | i_color *, i_color *); |
f1ac5027 | 162 | static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, |
efdc2568 | 163 | i_fcolor *, i_fcolor *); |
f1ac5027 | 164 | static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels, |
efdc2568 | 165 | i_color *, i_color *); |
f1ac5027 | 166 | static void fill_solidf_comb(i_fill_t *, int x, int y, int width, |
efdc2568 | 167 | int channels, i_fcolor *, i_fcolor *); |
f1ac5027 TC |
168 | |
169 | static i_fill_solid_t base_solid_fill = | |
170 | { | |
171 | { | |
172 | fill_solid, | |
173 | fill_solidf, | |
174 | NULL, | |
efdc2568 TC |
175 | NULL, |
176 | NULL, | |
f1ac5027 TC |
177 | }, |
178 | }; | |
179 | static i_fill_solid_t base_solid_fill_comb = | |
180 | { | |
181 | { | |
182 | fill_solid_comb, | |
183 | fill_solidf_comb, | |
184 | NULL, | |
efdc2568 TC |
185 | NULL, |
186 | NULL, | |
f1ac5027 TC |
187 | }, |
188 | }; | |
189 | ||
773bc121 TC |
190 | /* |
191 | =item i_fill_destroy(fill) | |
192 | ||
193 | Call to destroy any fill object. | |
194 | ||
195 | =cut | |
196 | */ | |
197 | ||
f1ac5027 TC |
198 | void |
199 | i_fill_destroy(i_fill_t *fill) { | |
200 | if (fill->destroy) | |
201 | (fill->destroy)(fill); | |
202 | myfree(fill); | |
203 | } | |
204 | ||
773bc121 TC |
205 | /* |
206 | =item i_new_fill_solidf(color, combine) | |
207 | ||
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 TC |
215 | i_fill_t * |
216 | i_new_fill_solidf(i_fcolor *c, int combine) { | |
217 | int ch; | |
218 | i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); | |
219 | ||
efdc2568 | 220 | if (combine && c->channel[3] < 1.0) { |
f1ac5027 | 221 | *fill = base_solid_fill_comb; |
efdc2568 TC |
222 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); |
223 | } | |
f1ac5027 TC |
224 | else |
225 | *fill = base_solid_fill; | |
226 | fill->fc = *c; | |
227 | for (ch = 0; ch < MAXCHANNELS; ++ch) { | |
228 | fill->c.channel[ch] = SampleFTo8(c->channel[ch]); | |
229 | } | |
230 | ||
231 | return &fill->base; | |
232 | } | |
233 | ||
773bc121 TC |
234 | /* |
235 | =item i_new_fill_solid(color, combine) | |
236 | ||
237 | Create a solid fill based. | |
238 | ||
239 | If combine is non-zero then alpha values will be combined. | |
240 | ||
241 | =cut | |
242 | */ | |
243 | ||
f1ac5027 TC |
244 | i_fill_t * |
245 | i_new_fill_solid(i_color *c, int combine) { | |
246 | int ch; | |
247 | i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); | |
248 | ||
efdc2568 | 249 | if (combine && c->channel[3] < 255) { |
f1ac5027 | 250 | *fill = base_solid_fill_comb; |
efdc2568 TC |
251 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); |
252 | } | |
f1ac5027 TC |
253 | else |
254 | *fill = base_solid_fill; | |
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]; | |
402 | int dx, dy; | |
403 | } i_fill_hatch_t; | |
404 | ||
405 | static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, | |
efdc2568 | 406 | i_color *data, i_color *work); |
f1ac5027 | 407 | static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, |
efdc2568 | 408 | i_fcolor *data, i_fcolor *work); |
773bc121 TC |
409 | static |
410 | i_fill_t * | |
411 | i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, | |
412 | int combine, int hatch, unsigned char *cust_hatch, | |
413 | int dx, int dy); | |
414 | ||
415 | /* | |
416 | =item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy) | |
417 | ||
418 | Creates a new hatched fill with the fg color used for the 1 bits in | |
419 | the hatch and bg for the 0 bits. If combine is non-zero alpha values | |
420 | will be combined. | |
f1ac5027 | 421 | |
773bc121 TC |
422 | If cust_hatch is non-NULL it should be a pointer to 8 bytes of the |
423 | hash definition, with the high-bits to the left. | |
424 | ||
425 | If cust_hatch is NULL then one of the standard hatches is used. | |
426 | ||
427 | (dx, dy) are an offset into the hatch which can be used to unalign adjoining areas, or to align the origin of a hatch with the the side of a filled area. | |
428 | ||
429 | =cut | |
430 | */ | |
431 | i_fill_t * | |
432 | i_new_fill_hatch(i_color *fg, i_color *bg, int combine, int hatch, | |
433 | unsigned char *cust_hatch, int dx, int dy) { | |
434 | return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, | |
435 | dx, dy); | |
436 | } | |
437 | ||
438 | /* | |
439 | =item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy) | |
440 | ||
441 | Creates a new hatched fill with the fg color used for the 1 bits in | |
442 | the hatch and bg for the 0 bits. If combine is non-zero alpha values | |
443 | will be combined. | |
444 | ||
445 | If cust_hatch is non-NULL it should be a pointer to 8 bytes of the | |
446 | hash definition, with the high-bits to the left. | |
447 | ||
448 | If cust_hatch is NULL then one of the standard hatches is used. | |
449 | ||
450 | (dx, dy) are an offset into the hatch which can be used to unalign adjoining areas, or to align the origin of a hatch with the the side of a filled area. | |
451 | ||
452 | =cut | |
453 | */ | |
454 | i_fill_t * | |
455 | i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch, | |
456 | unsigned char *cust_hatch, int dx, int dy) { | |
457 | return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, | |
458 | dx, dy); | |
459 | } | |
460 | ||
461 | #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill)) | |
462 | ||
463 | /* | |
464 | =back | |
465 | ||
466 | =head1 INTERNAL FUNCTIONS | |
467 | ||
468 | =over | |
469 | ||
470 | =item fill_solid(fill, x, y, width, channels, data) | |
471 | ||
472 | The 8-bit sample fill function for non-combining solid fills. | |
473 | ||
474 | =cut | |
475 | */ | |
476 | static void | |
477 | fill_solid(i_fill_t *fill, int x, int y, int width, int channels, | |
efdc2568 | 478 | i_color *data, i_color *work) { |
773bc121 TC |
479 | while (width-- > 0) { |
480 | *data++ = T_SOLID_FILL(fill)->c; | |
481 | } | |
482 | } | |
483 | ||
484 | /* | |
485 | =item fill_solid(fill, x, y, width, channels, data) | |
486 | ||
487 | The floating sample fill function for non-combining solid fills. | |
488 | ||
489 | =cut | |
490 | */ | |
491 | static void | |
492 | fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, | |
efdc2568 | 493 | i_fcolor *data, i_fcolor *work) { |
773bc121 TC |
494 | while (width-- > 0) { |
495 | *data++ = T_SOLID_FILL(fill)->fc; | |
496 | } | |
497 | } | |
498 | ||
499 | /* | |
500 | =item fill_solid_comb(fill, x, y, width, channels, data) | |
501 | ||
502 | The 8-bit sample fill function for combining solid fills. | |
503 | ||
504 | =cut | |
505 | */ | |
506 | static void | |
507 | fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, | |
efdc2568 | 508 | i_color *data, i_color *work) { |
773bc121 | 509 | i_color c = T_SOLID_FILL(fill)->c; |
efdc2568 TC |
510 | int count = width; |
511 | i_color *wstart = work; | |
773bc121 TC |
512 | |
513 | while (width-- > 0) { | |
efdc2568 | 514 | *work++ = c; |
773bc121 | 515 | } |
efdc2568 | 516 | (fill->combine)(data, wstart, channels, count); |
773bc121 TC |
517 | } |
518 | ||
519 | /* | |
520 | =item fill_solidf_comb(fill, x, y, width, channels, data) | |
521 | ||
522 | The floating sample fill function for combining solid fills. | |
523 | ||
524 | =cut | |
525 | */ | |
526 | static void | |
527 | fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, | |
efdc2568 | 528 | i_fcolor *data, i_fcolor *work) { |
773bc121 | 529 | i_fcolor c = T_SOLID_FILL(fill)->fc; |
efdc2568 TC |
530 | int count = width; |
531 | i_fcolor *wstart = work; | |
773bc121 TC |
532 | |
533 | while (width-- > 0) { | |
efdc2568 | 534 | *work++ = c; |
773bc121 | 535 | } |
efdc2568 | 536 | (fill->combinef)(data, wstart, channels, count); |
773bc121 TC |
537 | } |
538 | ||
539 | /* | |
540 | =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy) | |
541 | ||
542 | Implements creation of hatch fill objects. | |
543 | ||
544 | =cut | |
545 | */ | |
f1ac5027 TC |
546 | static |
547 | i_fill_t * | |
548 | i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, | |
549 | int combine, int hatch, unsigned char *cust_hatch, | |
550 | int dx, int dy) { | |
551 | i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); | |
552 | ||
553 | fill->base.fill_with_color = fill_hatch; | |
554 | fill->base.fill_with_fcolor = fill_hatchf; | |
555 | fill->base.destroy = NULL; | |
556 | fill->fg = fg ? *fg : fcolor_to_color(ffg); | |
557 | fill->bg = bg ? *bg : fcolor_to_color(fbg); | |
558 | fill->ffg = ffg ? *ffg : color_to_fcolor(fg); | |
559 | fill->fbg = fbg ? *fbg : color_to_fcolor(bg); | |
efdc2568 TC |
560 | if (combine && (fill->ffg.channel[0] < 1 || fill->fbg.channel[0] < 1)) { |
561 | i_get_combine(combine, &fill->base.combine, &fill->base.combinef); | |
562 | } | |
563 | else { | |
564 | fill->base.combine = NULL; | |
565 | fill->base.combinef = NULL; | |
566 | } | |
f1ac5027 TC |
567 | if (cust_hatch) { |
568 | memcpy(fill->hatch, cust_hatch, 8); | |
569 | } | |
570 | else { | |
571 | if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) | |
572 | hatch = 0; | |
573 | memcpy(fill->hatch, builtin_hatches[hatch], 8); | |
574 | } | |
575 | fill->dx = dx & 7; | |
576 | fill->dy = dy & 7; | |
577 | ||
578 | return &fill->base; | |
579 | } | |
580 | ||
773bc121 TC |
581 | /* |
582 | =item fill_hatch(fill, x, y, width, channels, data) | |
f1ac5027 | 583 | |
773bc121 | 584 | The 8-bit sample fill function for hatched fills. |
f1ac5027 | 585 | |
773bc121 TC |
586 | =back |
587 | */ | |
f1ac5027 | 588 | static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, |
efdc2568 | 589 | i_color *data, i_color *work) { |
f1ac5027 TC |
590 | i_fill_hatch_t *f = (i_fill_hatch_t *)fill; |
591 | int byte = f->hatch[(y + f->dy) & 7]; | |
592 | int xpos = (x + f->dx) & 7; | |
593 | int mask = 128 >> xpos; | |
594 | ||
efdc2568 TC |
595 | if (fill->combine) { |
596 | int count = width; | |
597 | i_color *wstart = work; | |
f1ac5027 | 598 | |
efdc2568 TC |
599 | while (count-- > 0) { |
600 | *work++ = (byte & mask) ? f->fg : f->bg; | |
601 | ||
602 | if ((mask >>= 1) == 0) | |
603 | mask = 128; | |
f1ac5027 | 604 | } |
efdc2568 TC |
605 | (fill->combine)(data, wstart, channels, width); |
606 | } | |
607 | else { | |
608 | while (width-- > 0) { | |
609 | *data++ = (byte & mask) ? f->fg : f->bg; | |
610 | ||
611 | if ((mask >>= 1) == 0) | |
612 | mask = 128; | |
f1ac5027 | 613 | } |
f1ac5027 TC |
614 | } |
615 | } | |
616 | ||
773bc121 TC |
617 | /* |
618 | =item fill_hatchf(fill, x, y, width, channels, data) | |
619 | ||
620 | The floating sample fill function for hatched fills. | |
621 | ||
622 | =back | |
623 | */ | |
f1ac5027 | 624 | static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, |
efdc2568 | 625 | i_fcolor *data, i_fcolor *work) { |
f1ac5027 TC |
626 | i_fill_hatch_t *f = (i_fill_hatch_t *)fill; |
627 | int byte = f->hatch[(y + f->dy) & 7]; | |
628 | int xpos = (x + f->dx) & 7; | |
629 | int mask = 128 >> xpos; | |
630 | ||
efdc2568 TC |
631 | if (fill->combinef) { |
632 | int count = width; | |
633 | i_fcolor *wstart = work; | |
634 | ||
635 | while (count-- > 0) { | |
636 | *work++ = (byte & mask) ? f->ffg : f->fbg; | |
637 | ||
638 | if ((mask >>= 1) == 0) | |
639 | mask = 128; | |
640 | } | |
641 | (fill->combinef)(data, wstart, channels, width); | |
642 | } | |
643 | else { | |
644 | while (width-- > 0) { | |
645 | *data++ = (byte & mask) ? f->ffg : f->fbg; | |
f1ac5027 | 646 | |
efdc2568 TC |
647 | if ((mask >>= 1) == 0) |
648 | mask = 128; | |
f1ac5027 | 649 | } |
efdc2568 TC |
650 | } |
651 | } | |
652 | ||
653 | static void combine_replace(i_color *, i_color *, int, int); | |
654 | static void combine_replacef(i_fcolor *, i_fcolor *, int, int); | |
655 | static void combine_alphablend(i_color *, i_color *, int, int); | |
656 | static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int); | |
657 | static void combine_mult(i_color *, i_color *, int, int); | |
658 | static void combine_multf(i_fcolor *, i_fcolor *, int, int); | |
659 | static void combine_dissolve(i_color *, i_color *, int, int); | |
660 | static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int); | |
661 | static void combine_add(i_color *, i_color *, int, int); | |
662 | static void combine_addf(i_fcolor *, i_fcolor *, int, int); | |
663 | static void combine_subtract(i_color *, i_color *, int, int); | |
664 | static void combine_subtractf(i_fcolor *, i_fcolor *, int, int); | |
665 | static void combine_diff(i_color *, i_color *, int, int); | |
666 | static void combine_difff(i_fcolor *, i_fcolor *, int, int); | |
667 | static void combine_darken(i_color *, i_color *, int, int); | |
668 | static void combine_darkenf(i_fcolor *, i_fcolor *, int, int); | |
669 | static void combine_lighten(i_color *, i_color *, int, int); | |
670 | static void combine_lightenf(i_fcolor *, i_fcolor *, int, int); | |
671 | static void combine_hue(i_color *, i_color *, int, int); | |
672 | static void combine_huef(i_fcolor *, i_fcolor *, int, int); | |
673 | static void combine_sat(i_color *, i_color *, int, int); | |
674 | static void combine_satf(i_fcolor *, i_fcolor *, int, int); | |
675 | static void combine_value(i_color *, i_color *, int, int); | |
676 | static void combine_valuef(i_fcolor *, i_fcolor *, int, int); | |
677 | static void combine_color(i_color *, i_color *, int, int); | |
678 | static void combine_colorf(i_fcolor *, i_fcolor *, int, int); | |
679 | ||
680 | struct i_combines { | |
681 | i_fill_combine_f combine; | |
682 | i_fill_combinef_f combinef; | |
683 | } combines[] = | |
684 | { | |
685 | { /* replace */ | |
686 | combine_replace, | |
687 | combine_replacef, | |
688 | }, | |
689 | { /* alpha blend */ | |
690 | combine_alphablend, | |
691 | combine_alphablendf, | |
692 | }, | |
693 | { | |
694 | /* multiply */ | |
695 | combine_mult, | |
696 | combine_multf, | |
697 | }, | |
698 | { | |
699 | /* dissolve */ | |
700 | combine_dissolve, | |
701 | combine_dissolvef, | |
702 | }, | |
703 | { | |
704 | /* add */ | |
705 | combine_add, | |
706 | combine_addf, | |
707 | }, | |
708 | { | |
709 | /* subtract */ | |
710 | combine_subtract, | |
711 | combine_subtractf, | |
712 | }, | |
713 | { | |
714 | /* diff */ | |
715 | combine_diff, | |
716 | combine_difff, | |
717 | }, | |
718 | { | |
719 | combine_lighten, | |
720 | combine_lightenf, | |
721 | }, | |
722 | { | |
723 | combine_darken, | |
724 | combine_darkenf, | |
725 | }, | |
726 | { | |
727 | combine_hue, | |
728 | combine_huef, | |
729 | }, | |
730 | { | |
731 | combine_sat, | |
732 | combine_satf, | |
733 | }, | |
734 | { | |
735 | combine_value, | |
736 | combine_valuef, | |
737 | }, | |
738 | { | |
739 | combine_color, | |
740 | combine_colorf, | |
741 | }, | |
742 | }; | |
743 | ||
744 | /* | |
745 | =item i_get_combine(combine, color_func, fcolor_func) | |
746 | ||
747 | =cut | |
748 | */ | |
749 | ||
750 | void i_get_combine(int combine, i_fill_combine_f *color_func, | |
751 | i_fill_combinef_f *fcolor_func) { | |
752 | if (combine < 0 || combine > sizeof(combines) / sizeof(*combines)) | |
753 | combine = 0; | |
754 | ||
755 | *color_func = combines[combine].combine; | |
756 | *fcolor_func = combines[combine].combinef; | |
757 | } | |
758 | ||
759 | static void combine_replace(i_color *out, i_color *in, int channels, int count) { | |
760 | while (count--) { | |
761 | *out++ = *in++; | |
762 | } | |
763 | } | |
764 | ||
765 | static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
766 | while (count--) { | |
767 | *out++ = *in++; | |
768 | } | |
769 | } | |
770 | ||
771 | static void combine_alphablend(i_color *out, i_color *in, int channels, int count) { | |
772 | while (count--) { | |
773 | COMBINE(*out, *in, channels); | |
774 | ++out; | |
775 | ++in; | |
776 | } | |
777 | } | |
778 | ||
779 | static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
780 | while (count--) { | |
781 | COMBINEF(*out, *in, channels); | |
782 | ++out; | |
783 | ++in; | |
784 | } | |
785 | } | |
786 | ||
787 | static void combine_mult(i_color *out, i_color *in, int channels, int count) { | |
788 | int ch; | |
789 | ||
790 | while (count--) { | |
791 | i_color c = *in; | |
792 | double mult[MAXCHANNELS]; | |
793 | mult[3] = in->channel[3]; | |
794 | for (ch = 0; ch < (channels); ++ch) { | |
795 | if (ch != 3) | |
796 | mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255); | |
797 | } | |
798 | COMBINEA(*out, mult, channels); | |
799 | ++out; | |
800 | ++in; | |
801 | } | |
802 | } | |
803 | ||
804 | static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
805 | int ch; | |
806 | ||
807 | while (count--) { | |
808 | i_fcolor c = *in; | |
809 | for (ch = 0; ch < channels; ++ch) { | |
810 | if (ch != 3) | |
811 | c.channel[ch] = out->channel[ch] * in->channel[ch]; | |
812 | } | |
813 | COMBINEF(*out, c, channels); | |
814 | ++out; | |
815 | ++in; | |
816 | } | |
817 | } | |
818 | ||
819 | static void combine_dissolve(i_color *out, i_color *in, int channels, int count) { | |
820 | int ch; | |
821 | ||
822 | while (count--) { | |
823 | if (in->channel[3] > rand() * (255.0 / RAND_MAX)) | |
824 | COMBINE(*out, *in, channels); | |
825 | ++out; | |
826 | ++in; | |
827 | } | |
828 | } | |
829 | ||
830 | static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
831 | int ch; | |
832 | ||
833 | while (count--) { | |
834 | if (in->channel[3] > rand() * (1.0 / RAND_MAX)) | |
835 | COMBINEF(*out, *in, channels); | |
836 | ++out; | |
837 | ++in; | |
838 | } | |
839 | } | |
840 | ||
841 | static void combine_add(i_color *out, i_color *in, int channels, int count) { | |
842 | int ch; | |
843 | ||
844 | while (count--) { | |
845 | i_color c = *in; | |
846 | for (ch = 0; ch < (channels); ++ch) { | |
847 | if (ch != 3) { | |
848 | int total = out->channel[ch] + in->channel[ch]; | |
849 | if (total > 255) | |
850 | total = 255; | |
851 | c.channel[ch] = total; | |
852 | } | |
853 | } | |
854 | COMBINE(*out, c, channels); | |
855 | ++out; | |
856 | ++in; | |
857 | } | |
858 | } | |
859 | ||
860 | static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
861 | int ch; | |
862 | ||
863 | while (count--) { | |
864 | i_fcolor c = *in; | |
865 | for (ch = 0; ch < (channels); ++ch) { | |
866 | if (ch != 3) { | |
867 | double total = out->channel[ch] + in->channel[ch]; | |
868 | if (total > 1.0) | |
869 | total = 1.0; | |
870 | out->channel[ch] = total; | |
871 | } | |
872 | } | |
873 | COMBINEF(*out, c, channels); | |
874 | ++out; | |
875 | ++in; | |
876 | } | |
877 | } | |
878 | ||
879 | static void combine_subtract(i_color *out, i_color *in, int channels, int count) { | |
880 | int ch; | |
881 | ||
882 | while (count--) { | |
883 | i_color c = *in; | |
884 | for (ch = 0; ch < (channels); ++ch) { | |
885 | if (ch != 3) { | |
886 | int total = out->channel[ch] - in->channel[ch]; | |
887 | if (total < 0) | |
888 | total = 0; | |
889 | c.channel[ch] = total; | |
890 | } | |
891 | } | |
892 | COMBINE(*out, c, channels); | |
893 | ++out; | |
894 | ++in; | |
895 | } | |
896 | } | |
897 | ||
898 | static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
899 | int ch; | |
900 | ||
901 | while (count--) { | |
902 | i_fcolor c = *in; | |
903 | for (ch = 0; ch < channels; ++ch) { | |
904 | if (ch != 3) { | |
905 | double total = out->channel[ch] - in->channel[ch]; | |
906 | if (total < 0) | |
907 | total = 0; | |
908 | c.channel[ch] = total; | |
909 | } | |
910 | } | |
911 | COMBINEF(*out, c, channels); | |
912 | ++out; | |
913 | ++in; | |
914 | } | |
915 | } | |
916 | ||
917 | static void combine_diff(i_color *out, i_color *in, int channels, int count) { | |
918 | int ch; | |
919 | ||
920 | while (count--) { | |
921 | i_color c = *in; | |
922 | for (ch = 0; ch < (channels); ++ch) { | |
923 | if (ch != 3) | |
924 | c.channel[ch] = abs(out->channel[ch] - in->channel[ch]); | |
925 | } | |
926 | COMBINE(*out, c, channels) | |
927 | ++out; | |
928 | ++in; | |
929 | } | |
930 | } | |
931 | ||
932 | static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
933 | int ch; | |
934 | ||
935 | while (count--) { | |
936 | i_fcolor c = *in; | |
937 | for (ch = 0; ch < (channels); ++ch) { | |
938 | if (ch != 3) | |
939 | c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]); | |
f1ac5027 | 940 | } |
efdc2568 TC |
941 | COMBINEF(*out, c, channels); |
942 | ++out; | |
943 | ++in; | |
f1ac5027 TC |
944 | } |
945 | } | |
773bc121 | 946 | |
efdc2568 TC |
947 | static void combine_darken(i_color *out, i_color *in, int channels, int count) { |
948 | int ch; | |
949 | ||
950 | while (count--) { | |
951 | for (ch = 0; ch < channels; ++ch) { | |
952 | if (ch != 3 && out->channel[ch] < in->channel[ch]) | |
953 | in->channel[ch] = out->channel[ch]; | |
954 | } | |
955 | COMBINE(*out, *in, channels); | |
956 | ++out; | |
957 | ++in; | |
958 | } | |
959 | } | |
960 | ||
961 | static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
962 | int ch; | |
963 | ||
964 | while (count--) { | |
965 | for (ch = 0; ch < channels; ++ch) { | |
966 | if (ch != 3 && out->channel[ch] < in->channel[ch]) | |
967 | in->channel[ch] = out->channel[ch]; | |
968 | } | |
969 | COMBINEF(*out, *in, channels); | |
970 | ++out; | |
971 | ++in; | |
972 | } | |
973 | } | |
974 | ||
975 | static void combine_lighten(i_color *out, i_color *in, int channels, int count) { | |
976 | int ch; | |
977 | ||
978 | while (count--) { | |
979 | for (ch = 0; ch < channels; ++ch) { | |
980 | if (ch != 3 && out->channel[ch] > in->channel[ch]) | |
981 | in->channel[ch] = out->channel[ch]; | |
982 | } | |
983 | COMBINE(*out, *in, channels); | |
984 | ++out; | |
985 | ++in; | |
986 | } | |
987 | } | |
988 | ||
989 | static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
990 | int ch; | |
991 | ||
992 | while (count--) { | |
993 | for (ch = 0; ch < channels; ++ch) { | |
994 | if (ch != 3 && out->channel[ch] > in->channel[ch]) | |
995 | in->channel[ch] = out->channel[ch]; | |
996 | } | |
997 | COMBINEF(*out, *in, channels); | |
998 | ++out; | |
999 | ++in; | |
1000 | } | |
1001 | } | |
1002 | ||
1003 | static void combine_hue(i_color *out, i_color *in, int channels, int count) { | |
1004 | while (count--) { | |
1005 | i_color c = *out; | |
1006 | i_rgb_to_hsv(&c); | |
1007 | i_rgb_to_hsv(in); | |
1008 | c.channel[0] = in->channel[0]; | |
1009 | i_hsv_to_rgb(&c); | |
1010 | COMBINE(*out, c, channels); | |
1011 | ++out; | |
1012 | ++in; | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
1017 | while (count--) { | |
1018 | i_fcolor c = *out; | |
1019 | i_rgb_to_hsvf(&c); | |
1020 | i_rgb_to_hsvf(in); | |
1021 | c.channel[0] = in->channel[0]; | |
1022 | i_hsv_to_rgbf(&c); | |
1023 | c.channel[3] = in->channel[3]; | |
1024 | COMBINEF(*out, c, channels); | |
1025 | ++out; | |
1026 | ++in; | |
1027 | } | |
1028 | } | |
1029 | ||
1030 | static void combine_sat(i_color *out, i_color *in, int channels, int count) { | |
1031 | while (count--) { | |
1032 | i_color c = *out; | |
1033 | i_rgb_to_hsv(&c); | |
1034 | i_rgb_to_hsv(in); | |
1035 | c.channel[1] = in->channel[1]; | |
1036 | i_hsv_to_rgb(&c); | |
1037 | c.channel[3] = in->channel[3]; | |
1038 | COMBINE(*out, c, channels); | |
1039 | ++out; | |
1040 | ++in; | |
1041 | } | |
1042 | } | |
1043 | ||
1044 | static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) { | |
1045 | while (count--) { | |
1046 | i_fcolor c = *out; | |
1047 | i_rgb_to_hsvf(&c); | |
1048 | i_rgb_to_hsvf(in); | |
1049 | c.channel[1] = in->channel[1]; | |
1050 | i_hsv_to_rgbf(&c); | |
1051 | c.channel[3] = in->channel[3]; | |
1052 | COMBINEF(*out, c, channels); | |
1053 | ++out; | |
1054 | ++in; | |
1055 | } | |
1056 | } | |
1057 | ||
1058 | static void combine_value(i_color *out, i_color *in, int channels, int count) { | |
1059 | while (count--) { | |
1060 | i_color c = *out; | |
1061 | i_rgb_to_hsv(&c); | |
1062 | i_rgb_to_hsv(in); | |
1063 | c.channel[2] = in->channel[2]; | |
1064 | i_hsv_to_rgb(&c); | |
1065 | c.channel[3] = in->channel[3]; | |
1066 | COMBINE(*out, c, channels); | |
1067 | ++out; | |
1068 | ++in; | |
1069 | } | |
1070 | } | |
1071 | ||
1072 | static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, | |
1073 | int count) { | |
1074 | while (count--) { | |
1075 | i_fcolor c = *out; | |
1076 | i_rgb_to_hsvf(&c); | |
1077 | i_rgb_to_hsvf(in); | |
1078 | c.channel[2] = in->channel[2]; | |
1079 | i_hsv_to_rgbf(&c); | |
1080 | c.channel[3] = in->channel[3]; | |
1081 | COMBINEF(*out, c, channels); | |
1082 | ++out; | |
1083 | ++in; | |
1084 | } | |
1085 | } | |
1086 | ||
1087 | static void combine_color(i_color *out, i_color *in, int channels, int count) { | |
1088 | while (count--) { | |
1089 | i_color c = *out; | |
1090 | i_rgb_to_hsv(&c); | |
1091 | i_rgb_to_hsv(in); | |
1092 | c.channel[0] = in->channel[0]; | |
1093 | c.channel[1] = in->channel[1]; | |
1094 | i_hsv_to_rgb(&c); | |
1095 | c.channel[3] = in->channel[3]; | |
1096 | COMBINE(*out, c, channels); | |
1097 | ++out; | |
1098 | ++in; | |
1099 | } | |
1100 | } | |
1101 | ||
1102 | static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, | |
1103 | int count) { | |
1104 | while (count--) { | |
1105 | i_fcolor c = *out; | |
1106 | i_rgb_to_hsvf(&c); | |
1107 | i_rgb_to_hsvf(in); | |
1108 | c.channel[0] = in->channel[0]; | |
1109 | c.channel[1] = in->channel[1]; | |
1110 | i_hsv_to_rgbf(&c); | |
1111 | c.channel[3] = in->channel[3]; | |
1112 | COMBINEF(*out, c, channels); | |
1113 | ++out; | |
1114 | ++in; | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | ||
773bc121 TC |
1119 | /* |
1120 | =back | |
1121 | ||
1122 | =head1 AUTHOR | |
1123 | ||
1124 | Tony Cook <tony@develop-help.com> | |
1125 | ||
1126 | =head1 SEE ALSO | |
1127 | ||
1128 | Imager(3) | |
1129 | ||
1130 | =cut | |
1131 | */ |