more to do
[imager.git] / fills.c
CommitLineData
92bda632
TC
1#include "imager.h"
2#include "imageri.h"
f1ac5027
TC
3
4/*
773bc121 5=head1 NAME
f1ac5027 6
773bc121 7fills.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);
f576ce7e 19 fill = i_new_fill_image(im, matrix, xoff, yoff, combine);
773bc121
TC
20 i_fill_destroy(fill);
21
22=head1 DESCRIPTION
23
24Implements the basic general fills, which can be used for filling some
25shapes and for flood fills.
26
27Each fill can implement up to 3 functions:
28
29=over
30
31=item fill_with_color
32
33called for fills on 8-bit images. This can be NULL in which case the
34fill_with_colorf function is called.
35
36=item fill_with_fcolor
37
38called for fills on non-8-bit images or when fill_with_color is NULL.
39
40=item destroy
41
42called by i_fill_destroy() if non-NULL, to release any extra resources
43that the fill may need.
44
45=back
46
47fill_with_color and fill_with_fcolor are basically the same function
48except that the first works with lines of i_color and the second with
49lines of i_fcolor.
50
51If the combines member if non-zero the line data is populated from the
52target image before calling fill_with_*color.
53
54fill_with_color needs to fill the I<data> parameter with the fill
55pixels. If combines is non-zero it the fill pixels should be combined
56with the existing data.
57
58The current fills are:
59
60=over
61
62=item *
63
64solid fill
65
66=item *
67
68hatched fill
69
70=item *
71
72fountain fill
73
74=back
75
76Fountain fill is implemented by L<filters.c>.
77
efdc2568
TC
78Other fills that could be implemented include:
79
80=over
81
82=item *
83
84image - an image tiled over the fill area, with an offset either
85horizontally or vertically.
86
87=item *
88
89checkerboard - combine 2 fills in a checkerboard
90
91=item *
92
93combine - combine the levels of 2 other fills based in the levels of
94an image
95
96=item *
97
98regmach - use the register machine to generate colors
99
100=back
101
773bc121
TC
102=over
103
104=cut
f1ac5027
TC
105*/
106
97ac0a96 107static i_color fcolor_to_color(const i_fcolor *c) {
f1ac5027
TC
108 int ch;
109 i_color out;
110
111 for (ch = 0; ch < MAXCHANNELS; ++ch)
112 out.channel[ch] = SampleFTo8(c->channel[ch]);
976efad5
TC
113
114 return out;
f1ac5027
TC
115}
116
97ac0a96 117static i_fcolor color_to_fcolor(const i_color *c) {
f1ac5027 118 int ch;
976efad5 119 i_fcolor out;
f1ac5027
TC
120
121 for (ch = 0; ch < MAXCHANNELS; ++ch)
122 out.channel[ch] = Sample8ToF(c->channel[ch]);
976efad5
TC
123
124 return out;
f1ac5027
TC
125}
126
efdc2568 127/* alpha combine in with out */
f1ac5027
TC
128#define COMBINE(out, in, channels) \
129 { \
130 int ch; \
131 for (ch = 0; ch < (channels); ++ch) { \
132 (out).channel[ch] = ((out).channel[ch] * (255 - (in).channel[3]) \
133 + (in).channel[ch] * (in).channel[3]) / 255; \
134 } \
135 }
136
efdc2568
TC
137/* alpha combine in with out, in this case in is a simple array of
138 samples, potentially not integers - the mult combiner uses doubles
139 for accuracy */
140#define COMBINEA(out, in, channels) \
141 { \
142 int ch; \
143 for (ch = 0; ch < (channels); ++ch) { \
144 (out).channel[ch] = ((out).channel[ch] * (255 - (in)[3]) \
145 + (in)[ch] * (in)[3]) / 255; \
146 } \
147 }
148
f1ac5027
TC
149#define COMBINEF(out, in, channels) \
150 { \
151 int ch; \
152 for (ch = 0; ch < (channels); ++ch) { \
153 (out).channel[ch] = (out).channel[ch] * (1.0 - (in).channel[3]) \
154 + (in).channel[ch] * (in).channel[3]; \
155 } \
156 }
157
efdc2568
TC
158typedef struct
159{
160 i_fill_t base;
161 i_color c;
162 i_fcolor fc;
163} i_fill_solid_t;
164
f1ac5027 165static void fill_solid(i_fill_t *, int x, int y, int width, int channels,
43c5dacb 166 i_color *);
f1ac5027 167static void fill_solidf(i_fill_t *, int x, int y, int width, int channels,
43c5dacb 168 i_fcolor *);
f1ac5027 169static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels,
43c5dacb 170 i_color *);
f1ac5027 171static void fill_solidf_comb(i_fill_t *, int x, int y, int width,
43c5dacb 172 int channels, i_fcolor *);
f1ac5027
TC
173
174static i_fill_solid_t base_solid_fill =
175{
176 {
177 fill_solid,
178 fill_solidf,
179 NULL,
efdc2568
TC
180 NULL,
181 NULL,
f1ac5027
TC
182 },
183};
184static i_fill_solid_t base_solid_fill_comb =
185{
186 {
187 fill_solid_comb,
188 fill_solidf_comb,
189 NULL,
efdc2568
TC
190 NULL,
191 NULL,
f1ac5027
TC
192 },
193};
194
773bc121
TC
195/*
196=item i_fill_destroy(fill)
197
92bda632
TC
198=category Fills
199
773bc121
TC
200Call to destroy any fill object.
201
202=cut
203*/
204
f1ac5027
TC
205void
206i_fill_destroy(i_fill_t *fill) {
207 if (fill->destroy)
208 (fill->destroy)(fill);
209 myfree(fill);
210}
211
773bc121
TC
212/*
213=item i_new_fill_solidf(color, combine)
214
92bda632
TC
215=category Fills
216
773bc121
TC
217Create a solid fill based on a float color.
218
219If combine is non-zero then alpha values will be combined.
220
221=cut
222*/
223
f1ac5027 224i_fill_t *
97ac0a96 225i_new_fill_solidf(const i_fcolor *c, int combine) {
f1ac5027 226 int ch;
f0960b14 227 i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
f1ac5027 228
141a6114 229 if (combine) {
f1ac5027 230 *fill = base_solid_fill_comb;
efdc2568
TC
231 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
232 }
f1ac5027
TC
233 else
234 *fill = base_solid_fill;
235 fill->fc = *c;
236 for (ch = 0; ch < MAXCHANNELS; ++ch) {
237 fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
238 }
239
240 return &fill->base;
241}
242
773bc121
TC
243/*
244=item i_new_fill_solid(color, combine)
245
92bda632
TC
246=category Fills
247
248Create a solid fill based on an 8-bit color.
773bc121
TC
249
250If combine is non-zero then alpha values will be combined.
251
252=cut
253*/
254
f1ac5027 255i_fill_t *
97ac0a96 256i_new_fill_solid(const i_color *c, int combine) {
f1ac5027 257 int ch;
f0960b14 258 i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
f1ac5027 259
141a6114 260 if (combine) {
f1ac5027 261 *fill = base_solid_fill_comb;
efdc2568
TC
262 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
263 }
f1ac5027
TC
264 else
265 *fill = base_solid_fill;
266 fill->c = *c;
267 for (ch = 0; ch < MAXCHANNELS; ++ch) {
268 fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
269 }
270
271 return &fill->base;
272}
273
f1ac5027
TC
274static unsigned char
275builtin_hatches[][8] =
276{
277 {
278 /* 1x1 checkerboard */
279 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
280 },
281 {
282 /* 2x2 checkerboard */
283 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33,
284 },
285 {
286 /* 4 x 4 checkerboard */
287 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
288 },
289 {
290 /* single vertical lines */
291 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
292 },
293 {
294 /* double vertical lines */
295 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
296 },
297 {
298 /* quad vertical lines */
299 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
300 },
301 {
302 /* single hlines */
303 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 },
305 {
306 /* double hlines */
307 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
308 },
309 {
310 /* quad hlines */
311 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
312 },
313 {
314 /* single / */
315 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
316 },
317 {
318 /* single \ */
319 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
320 },
321 {
322 /* double / */
323 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
324 },
325 {
326 /* double \ */
327 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11,
328 },
329 {
330 /* single grid */
331 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
332 },
333 {
334 /* double grid */
335 0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88,
336 },
337 {
338 /* quad grid */
339 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA,
340 },
341 {
342 /* single dots */
343 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344 },
345 {
346 /* 4 dots */
347 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
348 },
349 {
350 /* 16 dots */
351 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
352 },
353 {
354 /* simple stipple */
355 0x48, 0x84, 0x00, 0x00, 0x84, 0x48, 0x00, 0x00,
356 },
357 {
358 /* weave */
359 0x55, 0xFD, 0x05, 0xFD, 0x55, 0xDF, 0x50, 0xDF,
360 },
361 {
362 /* single cross hatch */
363 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x01,
364 },
365 {
366 /* double cross hatch */
367 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11,
368 },
369 {
370 /* vertical lozenge */
371 0x11, 0x11, 0x11, 0xAA, 0x44, 0x44, 0x44, 0xAA,
372 },
373 {
374 /* horizontal lozenge */
375 0x88, 0x70, 0x88, 0x07, 0x88, 0x70, 0x88, 0x07,
376 },
377 {
378 /* scales overlapping downwards */
7a606d29 379 0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3,
f1ac5027
TC
380 },
381 {
382 /* scales overlapping upwards */
7a606d29 383 0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01,
f1ac5027
TC
384 },
385 {
386 /* scales overlapping leftwards */
7a606d29 387 0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84,
f1ac5027
TC
388 },
389 {
390 /* scales overlapping rightwards */
7a606d29 391 0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1,
f1ac5027
TC
392 },
393 {
394 /* denser stipple */
395 0x44, 0x88, 0x22, 0x11, 0x44, 0x88, 0x22, 0x11,
396 },
397 {
398 /* L-shaped tiles */
399 0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
400 },
cc6483e0
TC
401 {
402 /* wider stipple */
403 0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
404 },
f1ac5027
TC
405};
406
407typedef struct
408{
409 i_fill_t base;
410 i_color fg, bg;
411 i_fcolor ffg, fbg;
412 unsigned char hatch[8];
413 int dx, dy;
414} i_fill_hatch_t;
415
416static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 417 i_color *data);
f1ac5027 418static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 419 i_fcolor *data);
773bc121
TC
420static
421i_fill_t *
97ac0a96
TC
422i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const i_fcolor *fbg,
423 int combine, int hatch, const unsigned char *cust_hatch,
773bc121
TC
424 int dx, int dy);
425
426/*
427=item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
428
92bda632
TC
429=category Fills
430
773bc121
TC
431Creates a new hatched fill with the fg color used for the 1 bits in
432the hatch and bg for the 0 bits. If combine is non-zero alpha values
433will be combined.
f1ac5027 434
773bc121
TC
435If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
436hash definition, with the high-bits to the left.
437
438If cust_hatch is NULL then one of the standard hatches is used.
439
440(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.
441
442=cut
443*/
444i_fill_t *
97ac0a96
TC
445i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch,
446 const unsigned char *cust_hatch, int dx, int dy) {
773bc121
TC
447 return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch,
448 dx, dy);
449}
450
451/*
452=item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
453
92bda632
TC
454=category Fills
455
773bc121
TC
456Creates a new hatched fill with the fg color used for the 1 bits in
457the hatch and bg for the 0 bits. If combine is non-zero alpha values
458will be combined.
459
460If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
461hash definition, with the high-bits to the left.
462
463If cust_hatch is NULL then one of the standard hatches is used.
464
465(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.
466
467=cut
468*/
469i_fill_t *
97ac0a96
TC
470i_new_fill_hatchf(const i_fcolor *fg, const i_fcolor *bg, int combine, int hatch,
471 const unsigned char *cust_hatch, int dx, int dy) {
773bc121
TC
472 return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch,
473 dx, dy);
474}
475
f576ce7e 476static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 477 i_color *data);
f576ce7e 478static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 479 i_fcolor *data);
f576ce7e
TC
480struct i_fill_image_t {
481 i_fill_t base;
482 i_img *src;
483 int xoff, yoff;
484 int has_matrix;
485 double matrix[9];
486};
487
488/*
489=item i_new_fill_image(im, matrix, xoff, yoff, combine)
490
92bda632
TC
491=category Fills
492
f576ce7e
TC
493Create an image based fill.
494
92bda632
TC
495matrix is an array of 9 doubles representing a transformation matrix.
496
497xoff and yoff are the offset into the image to start filling from.
498
f576ce7e
TC
499=cut
500*/
501i_fill_t *
97ac0a96 502i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combine) {
f0960b14 503 struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */
f576ce7e
TC
504
505 fill->base.fill_with_color = fill_image;
506 fill->base.fill_with_fcolor = fill_imagef;
507 fill->base.destroy = NULL;
508
509 if (combine) {
510 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
511 }
512 else {
513 fill->base.combine = NULL;
514 fill->base.combinef = NULL;
515 }
516 fill->src = im;
517 if (xoff < 0)
518 xoff += im->xsize;
519 fill->xoff = xoff;
520 if (yoff < 0)
521 yoff += im->ysize;
522 fill->yoff = yoff;
523 if (matrix) {
524 fill->has_matrix = 1;
525 memcpy(fill->matrix, matrix, sizeof(fill->matrix));
526 }
527 else
528 fill->has_matrix = 0;
529
530 return &fill->base;
531}
532
533
773bc121
TC
534#define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
535
536/*
537=back
538
539=head1 INTERNAL FUNCTIONS
540
541=over
542
543=item fill_solid(fill, x, y, width, channels, data)
544
545The 8-bit sample fill function for non-combining solid fills.
546
547=cut
548*/
549static void
550fill_solid(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 551 i_color *data) {
773bc121
TC
552 while (width-- > 0) {
553 *data++ = T_SOLID_FILL(fill)->c;
554 }
555}
556
557/*
558=item fill_solid(fill, x, y, width, channels, data)
559
560The floating sample fill function for non-combining solid fills.
561
562=cut
563*/
564static void
565fill_solidf(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 566 i_fcolor *data) {
773bc121
TC
567 while (width-- > 0) {
568 *data++ = T_SOLID_FILL(fill)->fc;
569 }
570}
571
572/*
573=item fill_solid_comb(fill, x, y, width, channels, data)
574
575The 8-bit sample fill function for combining solid fills.
576
577=cut
578*/
579static void
580fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 581 i_color *data) {
773bc121
TC
582 i_color c = T_SOLID_FILL(fill)->c;
583
584 while (width-- > 0) {
43c5dacb 585 *data++ = c;
773bc121
TC
586 }
587}
588
589/*
590=item fill_solidf_comb(fill, x, y, width, channels, data)
591
592The floating sample fill function for combining solid fills.
593
594=cut
595*/
596static void
597fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 598 i_fcolor *data) {
773bc121
TC
599 i_fcolor c = T_SOLID_FILL(fill)->fc;
600
601 while (width-- > 0) {
43c5dacb 602 *data++ = c;
773bc121
TC
603 }
604}
605
606/*
607=item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
608
609Implements creation of hatch fill objects.
610
611=cut
612*/
f1ac5027
TC
613static
614i_fill_t *
97ac0a96
TC
615i_new_hatch_low(const i_color *fg, const i_color *bg,
616 const i_fcolor *ffg, const i_fcolor *fbg,
617 int combine, int hatch, const unsigned char *cust_hatch,
f1ac5027 618 int dx, int dy) {
f0960b14 619 i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
f1ac5027
TC
620
621 fill->base.fill_with_color = fill_hatch;
622 fill->base.fill_with_fcolor = fill_hatchf;
623 fill->base.destroy = NULL;
05462f4b
TC
624 /* Some Sun C didn't like the condition expressions that were here.
625 See https://rt.cpan.org/Ticket/Display.html?id=21944
626 */
627 if (fg)
628 fill->fg = *fg;
629 else
630 fill->fg = fcolor_to_color(ffg);
631 if (bg)
632 fill->bg = *bg;
633 else
634 fill->bg = fcolor_to_color(fbg);
635 if (ffg)
636 fill->ffg = *ffg;
637 else
638 fill->ffg = color_to_fcolor(fg);
639 if (fbg)
640 fill->fbg = *fbg;
641 else
642 fill->fbg = color_to_fcolor(bg);
141a6114 643 if (combine) {
efdc2568
TC
644 i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
645 }
646 else {
647 fill->base.combine = NULL;
648 fill->base.combinef = NULL;
649 }
f1ac5027
TC
650 if (cust_hatch) {
651 memcpy(fill->hatch, cust_hatch, 8);
652 }
653 else {
654 if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches))
655 hatch = 0;
656 memcpy(fill->hatch, builtin_hatches[hatch], 8);
657 }
658 fill->dx = dx & 7;
659 fill->dy = dy & 7;
660
661 return &fill->base;
662}
663
773bc121
TC
664/*
665=item fill_hatch(fill, x, y, width, channels, data)
f1ac5027 666
773bc121 667The 8-bit sample fill function for hatched fills.
f1ac5027 668
b8c2033e 669=cut
773bc121 670*/
f1ac5027 671static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 672 i_color *data) {
f1ac5027
TC
673 i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
674 int byte = f->hatch[(y + f->dy) & 7];
675 int xpos = (x + f->dx) & 7;
676 int mask = 128 >> xpos;
677
43c5dacb
TC
678 while (width-- > 0) {
679 *data++ = (byte & mask) ? f->fg : f->bg;
680
681 if ((mask >>= 1) == 0)
682 mask = 128;
f1ac5027
TC
683 }
684}
685
773bc121
TC
686/*
687=item fill_hatchf(fill, x, y, width, channels, data)
688
689The floating sample fill function for hatched fills.
690
691=back
692*/
f1ac5027 693static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 694 i_fcolor *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;
699
43c5dacb
TC
700 while (width-- > 0) {
701 *data++ = (byte & mask) ? f->ffg : f->fbg;
702
703 if ((mask >>= 1) == 0)
704 mask = 128;
efdc2568
TC
705 }
706}
707
f576ce7e
TC
708/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */
709/* linear interpolation */
710static i_color interp_i_color(i_color before, i_color after, double pos,
711 int channels) {
712 i_color out;
713 int ch;
714
715 pos -= floor(pos);
716 for (ch = 0; ch < channels; ++ch)
717 out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
3efb0915 718 if (channels > 3 && out.channel[3])
f576ce7e
TC
719 for (ch = 0; ch < channels; ++ch)
720 if (ch != 3) {
721 int temp = out.channel[ch] * 255 / out.channel[3];
722 if (temp > 255)
723 temp = 255;
724 out.channel[ch] = temp;
725 }
726
727 return out;
728}
729
730/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */
731/* linear interpolation */
732static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
733 int channels) {
734 i_fcolor out;
735 int ch;
736
737 pos -= floor(pos);
738 for (ch = 0; ch < channels; ++ch)
739 out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
740 if (out.channel[3])
741 for (ch = 0; ch < channels; ++ch)
742 if (ch != 3) {
743 int temp = out.channel[ch] / out.channel[3];
744 if (temp > 1.0)
745 temp = 1.0;
746 out.channel[ch] = temp;
747 }
748
749 return out;
750}
751
752/*
753=item fill_image(fill, x, y, width, channels, data, work)
754
755=cut
756*/
757static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 758 i_color *data) {
f576ce7e 759 struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
f576ce7e 760 int i = 0;
cde2dbc7 761 i_color *out = data;
f576ce7e
TC
762
763 if (f->has_matrix) {
764 /* the hard way */
765 while (i < width) {
766 double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
767 double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
768 double ix = floor(rx / f->src->xsize);
769 double iy = floor(ry / f->src->ysize);
770 i_color c[2][2];
771 i_color c2[2];
772 int dy;
773
774 if (f->xoff) {
775 rx += iy * f->xoff;
776 ix = floor(rx / f->src->xsize);
777 }
778 else if (f->yoff) {
779 ry += ix * f->yoff;
780 iy = floor(ry / f->src->ysize);
781 }
782 rx -= ix * f->src->xsize;
783 ry -= iy * f->src->ysize;
784
785 for (dy = 0; dy < 2; ++dy) {
786 if ((int)rx == f->src->xsize-1) {
787 i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
788 i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
789 }
790 else {
791 i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize,
792 c[dy]);
793 }
794 c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
795 }
cde2dbc7 796 *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
f576ce7e
TC
797 ++i;
798 }
799 }
800 else {
801 /* the easy way */
802 /* this should be possible to optimize to use i_glin() */
803 while (i < width) {
804 int rx = x+i;
805 int ry = y;
806 int ix = rx / f->src->xsize;
807 int iy = ry / f->src->ysize;
808
809 if (f->xoff) {
810 rx += iy * f->xoff;
811 ix = rx / f->src->xsize;
812 }
813 else if (f->yoff) {
814 ry += ix * f->yoff;
815 iy = ry / f->src->xsize;
816 }
817 rx -= ix * f->src->xsize;
818 ry -= iy * f->src->ysize;
cde2dbc7
TC
819 i_gpix(f->src, rx, ry, out);
820 ++out;
f576ce7e
TC
821 ++i;
822 }
823 }
cde2dbc7
TC
824 if (f->src->channels == 3) {
825 /* just set the alpha */
826 for (i = 0; i < width; ++i) {
827 data->channel[3] = 255;
828 data++;
829 }
830 }
831 else if (f->src->channels == 2) {
832 /* copy the alpha to channel 3, duplicate the grey value */
833 for (i = 0; i < width; ++i) {
834 data->channel[3] = data->channel[1];
835 data->channel[1] = data->channel[2] = data->channel[0];
836 data++;
837 }
838 }
839 else if (f->src->channels == 1) {
840 /* set the alpha, duplicate grey */
841 for (i = 0; i < width; ++i) {
842 data->channel[3] = 255;
843 data->channel[1] = data->channel[2] = data->channel[0];
844 data++;
845 }
846 }
f576ce7e
TC
847}
848
849/*
850=item fill_image(fill, x, y, width, channels, data, work)
851
852=cut
853*/
854static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
43c5dacb 855 i_fcolor *data) {
f576ce7e 856 struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
f576ce7e 857 int i = 0;
f576ce7e
TC
858
859 if (f->has_matrix) {
860 /* the hard way */
861 while (i < width) {
862 double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
863 double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
864 double ix = floor(rx / f->src->xsize);
865 double iy = floor(ry / f->src->ysize);
866 i_fcolor c[2][2];
867 i_fcolor c2[2];
868 int dy;
869
870 if (f->xoff) {
871 rx += iy * f->xoff;
872 ix = floor(rx / f->src->xsize);
873 }
874 else if (f->yoff) {
875 ry += ix * f->yoff;
876 iy = floor(ry / f->src->ysize);
877 }
878 rx -= ix * f->src->xsize;
879 ry -= iy * f->src->ysize;
880
881 for (dy = 0; dy < 2; ++dy) {
882 if ((int)rx == f->src->xsize-1) {
883 i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
884 i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
885 }
886 else {
887 i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize,
888 c[dy]);
889 }
890 c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
891 }
43c5dacb 892 *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
f576ce7e
TC
893 ++i;
894 }
895 }
896 else {
897 /* the easy way */
898 /* this should be possible to optimize to use i_glin() */
899 while (i < width) {
900 int rx = x+i;
901 int ry = y;
902 int ix = rx / f->src->xsize;
903 int iy = ry / f->src->ysize;
904
905 if (f->xoff) {
906 rx += iy * f->xoff;
907 ix = rx / f->src->xsize;
908 }
909 else if (f->yoff) {
910 ry += ix * f->yoff;
911 iy = ry / f->src->xsize;
912 }
913 rx -= ix * f->src->xsize;
914 ry -= iy * f->src->ysize;
43c5dacb
TC
915 i_gpixf(f->src, rx, ry, data);
916 ++data;
f576ce7e
TC
917 ++i;
918 }
919 }
cde2dbc7
TC
920 if (f->src->channels == 3) {
921 /* just set the alpha */
922 for (i = 0; i < width; ++i) {
923 data->channel[3] = 1.0;
924 data++;
925 }
926 }
927 else if (f->src->channels == 2) {
928 /* copy the alpha to channel 3, duplicate the grey value */
929 for (i = 0; i < width; ++i) {
930 data->channel[3] = data->channel[1];
931 data->channel[1] = data->channel[2] = data->channel[0];
932 data++;
933 }
934 }
935 else if (f->src->channels == 1) {
936 /* set the alpha, duplicate grey */
937 for (i = 0; i < width; ++i) {
938 data->channel[3] = 1.0;
939 data->channel[1] = data->channel[2] = data->channel[0];
940 data++;
941 }
942 }
f576ce7e
TC
943}
944
efdc2568
TC
945static void combine_replace(i_color *, i_color *, int, int);
946static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
947static void combine_alphablend(i_color *, i_color *, int, int);
948static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
949static void combine_mult(i_color *, i_color *, int, int);
950static void combine_multf(i_fcolor *, i_fcolor *, int, int);
951static void combine_dissolve(i_color *, i_color *, int, int);
952static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
953static void combine_add(i_color *, i_color *, int, int);
954static void combine_addf(i_fcolor *, i_fcolor *, int, int);
955static void combine_subtract(i_color *, i_color *, int, int);
956static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
957static void combine_diff(i_color *, i_color *, int, int);
958static void combine_difff(i_fcolor *, i_fcolor *, int, int);
959static void combine_darken(i_color *, i_color *, int, int);
960static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
961static void combine_lighten(i_color *, i_color *, int, int);
962static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
963static void combine_hue(i_color *, i_color *, int, int);
964static void combine_huef(i_fcolor *, i_fcolor *, int, int);
965static void combine_sat(i_color *, i_color *, int, int);
966static void combine_satf(i_fcolor *, i_fcolor *, int, int);
967static void combine_value(i_color *, i_color *, int, int);
968static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
969static void combine_color(i_color *, i_color *, int, int);
970static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
971
b33c08f8 972static struct i_combines {
efdc2568
TC
973 i_fill_combine_f combine;
974 i_fill_combinef_f combinef;
975} combines[] =
976{
977 { /* replace */
978 combine_replace,
979 combine_replacef,
980 },
981 { /* alpha blend */
982 combine_alphablend,
983 combine_alphablendf,
984 },
985 {
986 /* multiply */
987 combine_mult,
988 combine_multf,
989 },
990 {
991 /* dissolve */
992 combine_dissolve,
993 combine_dissolvef,
994 },
995 {
996 /* add */
997 combine_add,
998 combine_addf,
999 },
1000 {
1001 /* subtract */
1002 combine_subtract,
1003 combine_subtractf,
1004 },
1005 {
1006 /* diff */
1007 combine_diff,
1008 combine_difff,
1009 },
1010 {
1011 combine_lighten,
1012 combine_lightenf,
1013 },
1014 {
1015 combine_darken,
1016 combine_darkenf,
1017 },
1018 {
1019 combine_hue,
1020 combine_huef,
1021 },
1022 {
1023 combine_sat,
1024 combine_satf,
1025 },
1026 {
1027 combine_value,
1028 combine_valuef,
1029 },
1030 {
1031 combine_color,
1032 combine_colorf,
1033 },
1034};
1035
1036/*
1037=item i_get_combine(combine, color_func, fcolor_func)
1038
1039=cut
1040*/
1041
1042void i_get_combine(int combine, i_fill_combine_f *color_func,
1043 i_fill_combinef_f *fcolor_func) {
1044 if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
1045 combine = 0;
1046
1047 *color_func = combines[combine].combine;
1048 *fcolor_func = combines[combine].combinef;
1049}
1050
1051static void combine_replace(i_color *out, i_color *in, int channels, int count) {
1052 while (count--) {
1053 *out++ = *in++;
1054 }
1055}
1056
1057static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1058 while (count--) {
1059 *out++ = *in++;
1060 }
1061}
1062
1063static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
1064 while (count--) {
1065 COMBINE(*out, *in, channels);
1066 ++out;
1067 ++in;
1068 }
1069}
1070
1071static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1072 while (count--) {
1073 COMBINEF(*out, *in, channels);
1074 ++out;
1075 ++in;
1076 }
1077}
1078
1079static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1080 int ch;
1081
1082 while (count--) {
efdc2568
TC
1083 double mult[MAXCHANNELS];
1084 mult[3] = in->channel[3];
1085 for (ch = 0; ch < (channels); ++ch) {
1086 if (ch != 3)
1087 mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
1088 }
1089 COMBINEA(*out, mult, channels);
1090 ++out;
1091 ++in;
1092 }
1093}
1094
1095static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1096 int ch;
1097
1098 while (count--) {
1099 i_fcolor c = *in;
1100 for (ch = 0; ch < channels; ++ch) {
1101 if (ch != 3)
1102 c.channel[ch] = out->channel[ch] * in->channel[ch];
1103 }
1104 COMBINEF(*out, c, channels);
1105 ++out;
1106 ++in;
1107 }
1108}
1109
1110static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
efdc2568
TC
1111 while (count--) {
1112 if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1113 COMBINE(*out, *in, channels);
1114 ++out;
1115 ++in;
1116 }
1117}
1118
1119static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
efdc2568
TC
1120 while (count--) {
1121 if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1122 COMBINEF(*out, *in, channels);
1123 ++out;
1124 ++in;
1125 }
1126}
1127
1128static void combine_add(i_color *out, i_color *in, int channels, int count) {
1129 int ch;
1130
1131 while (count--) {
1132 i_color c = *in;
1133 for (ch = 0; ch < (channels); ++ch) {
1134 if (ch != 3) {
1135 int total = out->channel[ch] + in->channel[ch];
1136 if (total > 255)
1137 total = 255;
1138 c.channel[ch] = total;
1139 }
1140 }
1141 COMBINE(*out, c, channels);
1142 ++out;
1143 ++in;
1144 }
1145}
1146
1147static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1148 int ch;
1149
1150 while (count--) {
1151 i_fcolor c = *in;
1152 for (ch = 0; ch < (channels); ++ch) {
1153 if (ch != 3) {
1154 double total = out->channel[ch] + in->channel[ch];
1155 if (total > 1.0)
1156 total = 1.0;
1157 out->channel[ch] = total;
1158 }
1159 }
1160 COMBINEF(*out, c, channels);
1161 ++out;
1162 ++in;
1163 }
1164}
1165
1166static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
1167 int ch;
1168
1169 while (count--) {
1170 i_color c = *in;
1171 for (ch = 0; ch < (channels); ++ch) {
1172 if (ch != 3) {
1173 int total = out->channel[ch] - in->channel[ch];
1174 if (total < 0)
1175 total = 0;
1176 c.channel[ch] = total;
1177 }
1178 }
1179 COMBINE(*out, c, channels);
1180 ++out;
1181 ++in;
1182 }
1183}
1184
1185static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1186 int ch;
1187
1188 while (count--) {
1189 i_fcolor c = *in;
1190 for (ch = 0; ch < channels; ++ch) {
1191 if (ch != 3) {
1192 double total = out->channel[ch] - in->channel[ch];
1193 if (total < 0)
1194 total = 0;
1195 c.channel[ch] = total;
1196 }
1197 }
1198 COMBINEF(*out, c, channels);
1199 ++out;
1200 ++in;
1201 }
1202}
1203
1204static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1205 int ch;
1206
1207 while (count--) {
1208 i_color c = *in;
1209 for (ch = 0; ch < (channels); ++ch) {
1210 if (ch != 3)
1211 c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1212 }
1213 COMBINE(*out, c, channels)
1214 ++out;
1215 ++in;
1216 }
1217}
1218
1219static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
1220 int ch;
1221
1222 while (count--) {
1223 i_fcolor c = *in;
1224 for (ch = 0; ch < (channels); ++ch) {
1225 if (ch != 3)
1226 c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
f1ac5027 1227 }
efdc2568
TC
1228 COMBINEF(*out, c, channels);
1229 ++out;
1230 ++in;
f1ac5027
TC
1231 }
1232}
773bc121 1233
efdc2568
TC
1234static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1235 int ch;
1236
1237 while (count--) {
1238 for (ch = 0; ch < channels; ++ch) {
1239 if (ch != 3 && out->channel[ch] < in->channel[ch])
1240 in->channel[ch] = out->channel[ch];
1241 }
1242 COMBINE(*out, *in, channels);
1243 ++out;
1244 ++in;
1245 }
1246}
1247
1248static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1249 int ch;
1250
1251 while (count--) {
1252 for (ch = 0; ch < channels; ++ch) {
1253 if (ch != 3 && out->channel[ch] < in->channel[ch])
1254 in->channel[ch] = out->channel[ch];
1255 }
1256 COMBINEF(*out, *in, channels);
1257 ++out;
1258 ++in;
1259 }
1260}
1261
1262static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1263 int ch;
1264
1265 while (count--) {
1266 for (ch = 0; ch < channels; ++ch) {
1267 if (ch != 3 && out->channel[ch] > in->channel[ch])
1268 in->channel[ch] = out->channel[ch];
1269 }
1270 COMBINE(*out, *in, channels);
1271 ++out;
1272 ++in;
1273 }
1274}
1275
1276static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1277 int ch;
1278
1279 while (count--) {
1280 for (ch = 0; ch < channels; ++ch) {
1281 if (ch != 3 && out->channel[ch] > in->channel[ch])
1282 in->channel[ch] = out->channel[ch];
1283 }
1284 COMBINEF(*out, *in, channels);
1285 ++out;
1286 ++in;
1287 }
1288}
1289
1290static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1291 while (count--) {
1292 i_color c = *out;
1293 i_rgb_to_hsv(&c);
1294 i_rgb_to_hsv(in);
1295 c.channel[0] = in->channel[0];
1296 i_hsv_to_rgb(&c);
976efad5 1297 c.channel[3] = in->channel[3];
efdc2568
TC
1298 COMBINE(*out, c, channels);
1299 ++out;
1300 ++in;
1301 }
1302}
1303
1304static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1305 while (count--) {
1306 i_fcolor c = *out;
1307 i_rgb_to_hsvf(&c);
1308 i_rgb_to_hsvf(in);
1309 c.channel[0] = in->channel[0];
1310 i_hsv_to_rgbf(&c);
1311 c.channel[3] = in->channel[3];
1312 COMBINEF(*out, c, channels);
1313 ++out;
1314 ++in;
1315 }
1316}
1317
1318static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1319 while (count--) {
1320 i_color c = *out;
1321 i_rgb_to_hsv(&c);
1322 i_rgb_to_hsv(in);
1323 c.channel[1] = in->channel[1];
1324 i_hsv_to_rgb(&c);
1325 c.channel[3] = in->channel[3];
1326 COMBINE(*out, c, channels);
1327 ++out;
1328 ++in;
1329 }
1330}
1331
1332static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1333 while (count--) {
1334 i_fcolor c = *out;
1335 i_rgb_to_hsvf(&c);
1336 i_rgb_to_hsvf(in);
1337 c.channel[1] = in->channel[1];
1338 i_hsv_to_rgbf(&c);
1339 c.channel[3] = in->channel[3];
1340 COMBINEF(*out, c, channels);
1341 ++out;
1342 ++in;
1343 }
1344}
1345
1346static void combine_value(i_color *out, i_color *in, int channels, int count) {
1347 while (count--) {
1348 i_color c = *out;
1349 i_rgb_to_hsv(&c);
1350 i_rgb_to_hsv(in);
1351 c.channel[2] = in->channel[2];
1352 i_hsv_to_rgb(&c);
1353 c.channel[3] = in->channel[3];
1354 COMBINE(*out, c, channels);
1355 ++out;
1356 ++in;
1357 }
1358}
1359
1360static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels,
1361 int count) {
1362 while (count--) {
1363 i_fcolor c = *out;
1364 i_rgb_to_hsvf(&c);
1365 i_rgb_to_hsvf(in);
1366 c.channel[2] = in->channel[2];
1367 i_hsv_to_rgbf(&c);
1368 c.channel[3] = in->channel[3];
1369 COMBINEF(*out, c, channels);
1370 ++out;
1371 ++in;
1372 }
1373}
1374
1375static void combine_color(i_color *out, i_color *in, int channels, int count) {
1376 while (count--) {
1377 i_color c = *out;
1378 i_rgb_to_hsv(&c);
1379 i_rgb_to_hsv(in);
1380 c.channel[0] = in->channel[0];
1381 c.channel[1] = in->channel[1];
1382 i_hsv_to_rgb(&c);
1383 c.channel[3] = in->channel[3];
1384 COMBINE(*out, c, channels);
1385 ++out;
1386 ++in;
1387 }
1388}
1389
1390static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels,
1391 int count) {
1392 while (count--) {
1393 i_fcolor c = *out;
1394 i_rgb_to_hsvf(&c);
1395 i_rgb_to_hsvf(in);
1396 c.channel[0] = in->channel[0];
1397 c.channel[1] = in->channel[1];
1398 i_hsv_to_rgbf(&c);
1399 c.channel[3] = in->channel[3];
1400 COMBINEF(*out, c, channels);
1401 ++out;
1402 ++in;
1403 }
1404}
1405
1406
773bc121
TC
1407/*
1408=back
1409
1410=head1 AUTHOR
1411
1412Tony Cook <tony@develop-help.com>
1413
1414=head1 SEE ALSO
1415
1416Imager(3)
1417
1418=cut
1419*/