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