- constify the Imager API
[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
200 Call to destroy any fill object.
201
202 =cut
203 */
204
205 void
206 i_fill_destroy(i_fill_t *fill) {
207   if (fill->destroy)
208     (fill->destroy)(fill);
209   myfree(fill);
210 }
211
212 /*
213 =item i_new_fill_solidf(color, combine)
214
215 =category Fills
216
217 Create a solid fill based on a float color.
218
219 If combine is non-zero then alpha values will be combined.
220
221 =cut
222 */
223
224 i_fill_t *
225 i_new_fill_solidf(const i_fcolor *c, int combine) {
226   int ch;
227   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
228   
229   if (combine) {
230     *fill = base_solid_fill_comb;
231     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
232   }
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
243 /*
244 =item i_new_fill_solid(color, combine)
245
246 =category Fills
247
248 Create a solid fill based on an 8-bit color.
249
250 If combine is non-zero then alpha values will be combined.
251
252 =cut
253 */
254
255 i_fill_t *
256 i_new_fill_solid(const i_color *c, int combine) {
257   int ch;
258   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
259
260   if (combine) {
261     *fill = base_solid_fill_comb;
262     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
263   }
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
274 static unsigned char
275 builtin_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 */
379     0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3,
380   },
381   {
382     /* scales overlapping upwards */
383     0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01,
384   },
385   {
386     /* scales overlapping leftwards */
387     0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84,
388   },
389   {
390     /* scales overlapping rightwards */
391     0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1,
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   },
401   {
402     /* wider stipple */
403     0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
404   },
405 };
406
407 typedef 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
416 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
417                        i_color *data);
418 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
419                         i_fcolor *data);
420 static
421 i_fill_t *
422 i_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,
424                 int dx, int dy);
425
426 /*
427 =item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
428
429 =category Fills
430
431 Creates a new hatched fill with the fg color used for the 1 bits in
432 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
433 will be combined.
434
435 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
436 hash definition, with the high-bits to the left.
437
438 If 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 */
444 i_fill_t *
445 i_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) {
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
454 =category Fills
455
456 Creates a new hatched fill with the fg color used for the 1 bits in
457 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
458 will be combined.
459
460 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
461 hash definition, with the high-bits to the left.
462
463 If 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 */
469 i_fill_t *
470 i_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) {
472   return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, 
473                          dx, dy);
474 }
475
476 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
477                        i_color *data);
478 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
479                        i_fcolor *data);
480 struct 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
491 =category Fills
492
493 Create an image based fill.
494
495 matrix is an array of 9 doubles representing a transformation matrix.
496
497 xoff and yoff are the offset into the image to start filling from.
498
499 =cut
500 */
501 i_fill_t *
502 i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combine) {
503   struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */
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
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
545 The 8-bit sample fill function for non-combining solid fills.
546
547 =cut
548 */
549 static void
550 fill_solid(i_fill_t *fill, int x, int y, int width, int channels, 
551            i_color *data) {
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
560 The floating sample fill function for non-combining solid fills.
561
562 =cut
563 */
564 static void
565 fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, 
566            i_fcolor *data) {
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
575 The 8-bit sample fill function for combining solid fills.
576
577 =cut
578 */
579 static void
580 fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, 
581                 i_color *data) {
582   i_color c = T_SOLID_FILL(fill)->c;
583
584   while (width-- > 0) {
585     *data++ = c;
586   }
587 }
588
589 /*
590 =item fill_solidf_comb(fill, x, y, width, channels, data)
591
592 The floating sample fill function for combining solid fills.
593
594 =cut
595 */
596 static void
597 fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, 
598            i_fcolor *data) {
599   i_fcolor c = T_SOLID_FILL(fill)->fc;
600
601   while (width-- > 0) {
602     *data++ = c;
603   }
604 }
605
606 /*
607 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
608
609 Implements creation of hatch fill objects.
610
611 =cut
612 */
613 static
614 i_fill_t *
615 i_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,
618                 int dx, int dy) {
619   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
620
621   fill->base.fill_with_color = fill_hatch;
622   fill->base.fill_with_fcolor = fill_hatchf;
623   fill->base.destroy = NULL;
624   fill->fg = fg ? *fg : fcolor_to_color(ffg);
625   fill->bg = bg ? *bg : fcolor_to_color(fbg);
626   fill->ffg = ffg ? *ffg : color_to_fcolor(fg);
627   fill->fbg = fbg ? *fbg : color_to_fcolor(bg);
628   if (combine) {
629     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
630   }
631   else {
632     fill->base.combine = NULL;
633     fill->base.combinef = NULL;
634   }
635   if (cust_hatch) {
636     memcpy(fill->hatch, cust_hatch, 8);
637   }
638   else {
639     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
640       hatch = 0;
641     memcpy(fill->hatch, builtin_hatches[hatch], 8);
642   }
643   fill->dx = dx & 7;
644   fill->dy = dy & 7;
645
646   return &fill->base;
647 }
648
649 /*
650 =item fill_hatch(fill, x, y, width, channels, data)
651
652 The 8-bit sample fill function for hatched fills.
653
654 =cut
655 */
656 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
657                        i_color *data) {
658   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
659   int byte = f->hatch[(y + f->dy) & 7];
660   int xpos = (x + f->dx) & 7;
661   int mask = 128 >> xpos;
662
663   while (width-- > 0) {
664     *data++ = (byte & mask) ? f->fg : f->bg;
665     
666     if ((mask >>= 1) == 0)
667       mask = 128;
668   }
669 }
670
671 /*
672 =item fill_hatchf(fill, x, y, width, channels, data)
673
674 The floating sample fill function for hatched fills.
675
676 =back
677 */
678 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
679                         i_fcolor *data) {
680   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
681   int byte = f->hatch[(y + f->dy) & 7];
682   int xpos = (x + f->dx) & 7;
683   int mask = 128 >> xpos;
684   
685   while (width-- > 0) {
686     *data++ = (byte & mask) ? f->ffg : f->fbg;
687     
688     if ((mask >>= 1) == 0)
689       mask = 128;
690   }
691 }
692
693 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
694 /* linear interpolation */
695 static i_color interp_i_color(i_color before, i_color after, double pos,
696                               int channels) {
697   i_color out;
698   int ch;
699
700   pos -= floor(pos);
701   for (ch = 0; ch < channels; ++ch)
702     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
703   if (out.channel[3])
704     for (ch = 0; ch < channels; ++ch)
705       if (ch != 3) {
706         int temp = out.channel[ch] * 255 / out.channel[3];
707         if (temp > 255)
708           temp = 255;
709         out.channel[ch] = temp;
710       }
711
712   return out;
713 }
714
715 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
716 /* linear interpolation */
717 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
718                                 int channels) {
719   i_fcolor out;
720   int ch;
721
722   pos -= floor(pos);
723   for (ch = 0; ch < channels; ++ch)
724     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
725   if (out.channel[3])
726     for (ch = 0; ch < channels; ++ch)
727       if (ch != 3) {
728         int temp = out.channel[ch] / out.channel[3];
729         if (temp > 1.0)
730           temp = 1.0;
731         out.channel[ch] = temp;
732       }
733
734   return out;
735 }
736
737 /*
738 =item fill_image(fill, x, y, width, channels, data, work)
739
740 =cut
741 */
742 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
743                        i_color *data) {
744   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
745   int i = 0;
746   i_color *out = data;
747   
748   if (f->has_matrix) {
749     /* the hard way */
750     while (i < width) {
751       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
752       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
753       double ix = floor(rx / f->src->xsize);
754       double iy = floor(ry / f->src->ysize);
755       i_color c[2][2];
756       i_color c2[2];
757       int dy;
758
759       if (f->xoff) {
760         rx += iy * f->xoff;
761         ix = floor(rx / f->src->xsize);
762       }
763       else if (f->yoff) {
764         ry += ix * f->yoff;
765         iy = floor(ry / f->src->ysize);
766       }
767       rx -= ix * f->src->xsize;
768       ry -= iy * f->src->ysize;
769
770       for (dy = 0; dy < 2; ++dy) {
771         if ((int)rx == f->src->xsize-1) {
772           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
773           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
774         }
775         else {
776           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
777                  c[dy]);
778         }
779         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
780       }
781       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
782       ++i;
783     }
784   }
785   else {
786     /* the easy way */
787     /* this should be possible to optimize to use i_glin() */
788     while (i < width) {
789       int rx = x+i;
790       int ry = y;
791       int ix = rx / f->src->xsize;
792       int iy = ry / f->src->ysize;
793
794       if (f->xoff) {
795         rx += iy * f->xoff;
796         ix = rx / f->src->xsize;
797       }
798       else if (f->yoff) {
799         ry += ix * f->yoff;
800         iy = ry / f->src->xsize;
801       }
802       rx -= ix * f->src->xsize;
803       ry -= iy * f->src->ysize;
804       i_gpix(f->src, rx, ry, out);
805       ++out;
806       ++i;
807     }
808   }
809   if (f->src->channels == 3) {
810     /* just set the alpha */
811     for (i = 0; i <  width; ++i) {
812       data->channel[3] = 255;
813       data++;
814     }
815   }
816   else if (f->src->channels == 2) {
817     /* copy the alpha to channel 3, duplicate the grey value */
818     for (i = 0; i <  width; ++i) {
819       data->channel[3] = data->channel[1];
820       data->channel[1] = data->channel[2] = data->channel[0];
821       data++;
822     }
823   }
824   else if (f->src->channels == 1) {
825     /* set the alpha, duplicate grey */
826     for (i = 0; i <  width; ++i) {
827       data->channel[3] = 255;
828       data->channel[1] = data->channel[2] = data->channel[0];
829       data++;
830     }
831   }
832 }
833
834 /*
835 =item fill_image(fill, x, y, width, channels, data, work)
836
837 =cut
838 */
839 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
840                        i_fcolor *data) {
841   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
842   int i = 0;
843   
844   if (f->has_matrix) {
845     /* the hard way */
846     while (i < width) {
847       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
848       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
849       double ix = floor(rx / f->src->xsize);
850       double iy = floor(ry / f->src->ysize);
851       i_fcolor c[2][2];
852       i_fcolor c2[2];
853       int dy;
854
855       if (f->xoff) {
856         rx += iy * f->xoff;
857         ix = floor(rx / f->src->xsize);
858       }
859       else if (f->yoff) {
860         ry += ix * f->yoff;
861         iy = floor(ry / f->src->ysize);
862       }
863       rx -= ix * f->src->xsize;
864       ry -= iy * f->src->ysize;
865
866       for (dy = 0; dy < 2; ++dy) {
867         if ((int)rx == f->src->xsize-1) {
868           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
869           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
870         }
871         else {
872           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
873                  c[dy]);
874         }
875         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
876       }
877       *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
878       ++i;
879     }
880   }
881   else {
882     /* the easy way */
883     /* this should be possible to optimize to use i_glin() */
884     while (i < width) {
885       int rx = x+i;
886       int ry = y;
887       int ix = rx / f->src->xsize;
888       int iy = ry / f->src->ysize;
889
890       if (f->xoff) {
891         rx += iy * f->xoff;
892         ix = rx / f->src->xsize;
893       }
894       else if (f->yoff) {
895         ry += ix * f->yoff;
896         iy = ry / f->src->xsize;
897       }
898       rx -= ix * f->src->xsize;
899       ry -= iy * f->src->ysize;
900       i_gpixf(f->src, rx, ry, data);
901       ++data;
902       ++i;
903     }
904   }
905   if (f->src->channels == 3) {
906     /* just set the alpha */
907     for (i = 0; i <  width; ++i) {
908       data->channel[3] = 1.0;
909       data++;
910     }
911   }
912   else if (f->src->channels == 2) {
913     /* copy the alpha to channel 3, duplicate the grey value */
914     for (i = 0; i <  width; ++i) {
915       data->channel[3] = data->channel[1];
916       data->channel[1] = data->channel[2] = data->channel[0];
917       data++;
918     }
919   }
920   else if (f->src->channels == 1) {
921     /* set the alpha, duplicate grey */
922     for (i = 0; i <  width; ++i) {
923       data->channel[3] = 1.0;
924       data->channel[1] = data->channel[2] = data->channel[0];
925       data++;
926     }
927   }
928 }
929
930 static void combine_replace(i_color *, i_color *, int, int);
931 static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
932 static void combine_alphablend(i_color *, i_color *, int, int);
933 static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
934 static void combine_mult(i_color *, i_color *, int, int);
935 static void combine_multf(i_fcolor *, i_fcolor *, int, int);
936 static void combine_dissolve(i_color *, i_color *, int, int);
937 static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
938 static void combine_add(i_color *, i_color *, int, int);
939 static void combine_addf(i_fcolor *, i_fcolor *, int, int);
940 static void combine_subtract(i_color *, i_color *, int, int);
941 static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
942 static void combine_diff(i_color *, i_color *, int, int);
943 static void combine_difff(i_fcolor *, i_fcolor *, int, int);
944 static void combine_darken(i_color *, i_color *, int, int);
945 static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
946 static void combine_lighten(i_color *, i_color *, int, int);
947 static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
948 static void combine_hue(i_color *, i_color *, int, int);
949 static void combine_huef(i_fcolor *, i_fcolor *, int, int);
950 static void combine_sat(i_color *, i_color *, int, int);
951 static void combine_satf(i_fcolor *, i_fcolor *, int, int);
952 static void combine_value(i_color *, i_color *, int, int);
953 static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
954 static void combine_color(i_color *, i_color *, int, int);
955 static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
956
957 static struct i_combines {
958   i_fill_combine_f combine;
959   i_fill_combinef_f combinef;
960 } combines[] =
961 {
962   { /* replace */
963     combine_replace,
964     combine_replacef,
965   },
966   { /* alpha blend */
967     combine_alphablend,
968     combine_alphablendf,
969   },
970   {
971     /* multiply */
972     combine_mult,
973     combine_multf,
974   },
975   {
976     /* dissolve */
977     combine_dissolve,
978     combine_dissolvef,
979   },
980   {
981     /* add */
982     combine_add,
983     combine_addf,
984   },
985   {
986     /* subtract */
987     combine_subtract,
988     combine_subtractf,
989   },
990   {
991     /* diff */
992     combine_diff,
993     combine_difff,
994   },
995   {
996     combine_lighten,
997     combine_lightenf,
998   },
999   {
1000     combine_darken,
1001     combine_darkenf,
1002   },
1003   {
1004     combine_hue,
1005     combine_huef,
1006   },
1007   {
1008     combine_sat,
1009     combine_satf,
1010   },
1011   {
1012     combine_value,
1013     combine_valuef,
1014   },
1015   {
1016     combine_color,
1017     combine_colorf,
1018   },
1019 };
1020
1021 /*
1022 =item i_get_combine(combine, color_func, fcolor_func)
1023
1024 =cut
1025 */
1026
1027 void i_get_combine(int combine, i_fill_combine_f *color_func, 
1028                    i_fill_combinef_f *fcolor_func) {
1029   if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
1030     combine = 0;
1031
1032   *color_func = combines[combine].combine;
1033   *fcolor_func = combines[combine].combinef;
1034 }
1035
1036 static void combine_replace(i_color *out, i_color *in, int channels, int count) {
1037   while (count--) {
1038     *out++ = *in++;
1039   }
1040 }
1041
1042 static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1043   while (count--) {
1044     *out++ = *in++;
1045   }
1046 }
1047
1048 static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
1049   while (count--) {
1050     COMBINE(*out, *in, channels);
1051     ++out;
1052     ++in;
1053   }
1054 }
1055
1056 static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1057   while (count--) {
1058     COMBINEF(*out, *in, channels);
1059     ++out;
1060     ++in;
1061   }
1062 }
1063
1064 static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1065   int ch;
1066
1067   while (count--) {
1068     double mult[MAXCHANNELS];
1069     mult[3] = in->channel[3];
1070     for (ch = 0; ch < (channels); ++ch) { 
1071       if (ch != 3)
1072         mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
1073     } 
1074     COMBINEA(*out, mult, channels);
1075     ++out;
1076     ++in;
1077   }
1078 }
1079
1080 static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1081   int ch;
1082
1083   while (count--) {
1084     i_fcolor c = *in;
1085     for (ch = 0; ch < channels; ++ch) { 
1086       if (ch != 3)
1087         c.channel[ch] = out->channel[ch] * in->channel[ch];
1088     } 
1089     COMBINEF(*out, c, channels);
1090     ++out;
1091     ++in;
1092   }
1093 }
1094
1095 static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
1096   while (count--) {
1097     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1098       COMBINE(*out, *in, channels);
1099     ++out;
1100     ++in;
1101   }
1102 }
1103
1104 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1105   while (count--) {
1106     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1107       COMBINEF(*out, *in, channels);
1108     ++out;
1109     ++in;
1110   }
1111 }
1112
1113 static void combine_add(i_color *out, i_color *in, int channels, int count) {
1114   int ch;
1115
1116   while (count--) {
1117     i_color c = *in;
1118     for (ch = 0; ch < (channels); ++ch) { 
1119       if (ch != 3) {
1120         int total = out->channel[ch] + in->channel[ch];
1121         if (total > 255)
1122           total = 255;
1123         c.channel[ch] = total;
1124       }
1125     } 
1126     COMBINE(*out, c, channels);
1127     ++out;
1128     ++in;
1129   }
1130 }
1131
1132 static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1133   int ch;
1134
1135   while (count--) {
1136     i_fcolor c = *in;
1137     for (ch = 0; ch < (channels); ++ch) { 
1138       if (ch != 3) {
1139         double total = out->channel[ch] + in->channel[ch];
1140         if (total > 1.0)
1141           total = 1.0;
1142         out->channel[ch] = total;
1143       }
1144     } 
1145     COMBINEF(*out, c, channels);
1146     ++out;
1147     ++in;
1148   }
1149 }
1150
1151 static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
1152   int ch;
1153
1154   while (count--) {
1155     i_color c = *in;
1156     for (ch = 0; ch < (channels); ++ch) { 
1157       if (ch != 3) {
1158         int total = out->channel[ch] - in->channel[ch];
1159         if (total < 0)
1160           total = 0;
1161         c.channel[ch] = total;
1162       }
1163     } 
1164     COMBINE(*out, c, channels);
1165     ++out;
1166     ++in;
1167   }
1168 }
1169
1170 static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1171   int ch;
1172
1173   while (count--) {
1174     i_fcolor c = *in;
1175     for (ch = 0; ch < channels; ++ch) { 
1176       if (ch != 3) {
1177         double total = out->channel[ch] - in->channel[ch];
1178         if (total < 0)
1179           total = 0;
1180         c.channel[ch] = total;
1181       }
1182     } 
1183     COMBINEF(*out, c, channels);
1184     ++out;
1185     ++in;
1186   }
1187 }
1188
1189 static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1190   int ch;
1191
1192   while (count--) {
1193     i_color c = *in;
1194     for (ch = 0; ch < (channels); ++ch) { 
1195       if (ch != 3) 
1196         c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1197     } 
1198     COMBINE(*out, c, channels)
1199     ++out;
1200     ++in;
1201   }
1202 }
1203
1204 static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
1205   int ch;
1206
1207   while (count--) {
1208     i_fcolor c = *in;
1209     for (ch = 0; ch < (channels); ++ch) { 
1210       if (ch != 3)
1211         c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
1212     }
1213     COMBINEF(*out, c, channels);
1214     ++out;
1215     ++in;
1216   }
1217 }
1218
1219 static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1220   int ch;
1221
1222   while (count--) {
1223     for (ch = 0; ch < channels; ++ch) { 
1224       if (ch != 3 && out->channel[ch] < in->channel[ch])
1225         in->channel[ch] = out->channel[ch];
1226     } 
1227     COMBINE(*out, *in, channels);
1228     ++out;
1229     ++in;
1230   }
1231 }
1232
1233 static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1234   int ch;
1235
1236   while (count--) {
1237     for (ch = 0; ch < channels; ++ch) { 
1238       if (ch != 3 && out->channel[ch] < in->channel[ch])
1239         in->channel[ch] = out->channel[ch];
1240     } 
1241     COMBINEF(*out, *in, channels);
1242     ++out;
1243     ++in;
1244   }
1245 }
1246
1247 static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1248   int ch;
1249
1250   while (count--) {
1251     for (ch = 0; ch < channels; ++ch) { 
1252       if (ch != 3 && out->channel[ch] > in->channel[ch])
1253         in->channel[ch] = out->channel[ch];
1254     } 
1255     COMBINE(*out, *in, channels);
1256     ++out;
1257     ++in;
1258   }
1259 }
1260
1261 static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1262   int ch;
1263
1264   while (count--) {
1265     for (ch = 0; ch < channels; ++ch) { 
1266       if (ch != 3 && out->channel[ch] > in->channel[ch])
1267         in->channel[ch] = out->channel[ch];
1268     } 
1269     COMBINEF(*out, *in, channels);
1270     ++out;
1271     ++in;
1272   }
1273 }
1274
1275 static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1276   while (count--) {
1277     i_color c = *out;
1278     i_rgb_to_hsv(&c);
1279     i_rgb_to_hsv(in);
1280     c.channel[0] = in->channel[0];
1281     i_hsv_to_rgb(&c);
1282     c.channel[3] = in->channel[3];
1283     COMBINE(*out, c, channels);
1284     ++out;
1285     ++in;
1286   }
1287 }
1288
1289 static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1290   while (count--) {
1291     i_fcolor c = *out;
1292     i_rgb_to_hsvf(&c);
1293     i_rgb_to_hsvf(in);
1294     c.channel[0] = in->channel[0];
1295     i_hsv_to_rgbf(&c);
1296     c.channel[3] = in->channel[3];
1297     COMBINEF(*out, c, channels);
1298     ++out;
1299     ++in;
1300   }
1301 }
1302
1303 static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1304   while (count--) {
1305     i_color c = *out;
1306     i_rgb_to_hsv(&c);
1307     i_rgb_to_hsv(in);
1308     c.channel[1] = in->channel[1];
1309     i_hsv_to_rgb(&c);
1310     c.channel[3] = in->channel[3];
1311     COMBINE(*out, c, channels);
1312     ++out;
1313     ++in;
1314   }
1315 }
1316
1317 static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1318   while (count--) {
1319     i_fcolor c = *out;
1320     i_rgb_to_hsvf(&c);
1321     i_rgb_to_hsvf(in);
1322     c.channel[1] = in->channel[1];
1323     i_hsv_to_rgbf(&c);
1324     c.channel[3] = in->channel[3];
1325     COMBINEF(*out, c, channels);
1326     ++out;
1327     ++in;
1328   }
1329 }
1330
1331 static void combine_value(i_color *out, i_color *in, int channels, int count) {
1332   while (count--) {
1333     i_color c = *out;
1334     i_rgb_to_hsv(&c);
1335     i_rgb_to_hsv(in);
1336     c.channel[2] = in->channel[2];
1337     i_hsv_to_rgb(&c);
1338     c.channel[3] = in->channel[3];
1339     COMBINE(*out, c, channels);
1340     ++out;
1341     ++in;
1342   }
1343 }
1344
1345 static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
1346                            int count) {
1347   while (count--) {
1348     i_fcolor c = *out;
1349     i_rgb_to_hsvf(&c);
1350     i_rgb_to_hsvf(in);
1351     c.channel[2] = in->channel[2];
1352     i_hsv_to_rgbf(&c);
1353     c.channel[3] = in->channel[3];
1354     COMBINEF(*out, c, channels);
1355     ++out;
1356     ++in;
1357   }
1358 }
1359
1360 static void combine_color(i_color *out, i_color *in, int channels, int count) {
1361   while (count--) {
1362     i_color c = *out;
1363     i_rgb_to_hsv(&c);
1364     i_rgb_to_hsv(in);
1365     c.channel[0] = in->channel[0];
1366     c.channel[1] = in->channel[1];
1367     i_hsv_to_rgb(&c);
1368     c.channel[3] = in->channel[3];
1369     COMBINE(*out, c, channels);
1370     ++out;
1371     ++in;
1372   }
1373 }
1374
1375 static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
1376                            int count) {
1377   while (count--) {
1378     i_fcolor c = *out;
1379     i_rgb_to_hsvf(&c);
1380     i_rgb_to_hsvf(in);
1381     c.channel[0] = in->channel[0];
1382     c.channel[1] = in->channel[1];
1383     i_hsv_to_rgbf(&c);
1384     c.channel[3] = in->channel[3];
1385     COMBINEF(*out, c, channels);
1386     ++out;
1387     ++in;
1388   }
1389 }
1390
1391
1392 /*
1393 =back
1394
1395 =head1 AUTHOR
1396
1397 Tony Cook <tony@develop-help.com>
1398
1399 =head1 SEE ALSO
1400
1401 Imager(3)
1402
1403 =cut
1404 */