]> git.imager.perl.org - imager.git/blob - fills.c
difference() method
[imager.git] / fills.c
1 #include "image.h"
2 #include "imagei.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(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(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 Call to destroy any fill object.
199
200 =cut
201 */
202
203 void
204 i_fill_destroy(i_fill_t *fill) {
205   if (fill->destroy)
206     (fill->destroy)(fill);
207   myfree(fill);
208 }
209
210 /*
211 =item i_new_fill_solidf(color, combine)
212
213 Create a solid fill based on a float color.
214
215 If combine is non-zero then alpha values will be combined.
216
217 =cut
218 */
219
220 i_fill_t *
221 i_new_fill_solidf(i_fcolor *c, int combine) {
222   int ch;
223   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t));
224   
225   if (combine) {
226     *fill = base_solid_fill_comb;
227     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
228   }
229   else
230     *fill = base_solid_fill;
231   fill->fc = *c;
232   for (ch = 0; ch < MAXCHANNELS; ++ch) {
233     fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
234   }
235   
236   return &fill->base;
237 }
238
239 /*
240 =item i_new_fill_solid(color, combine)
241
242 Create a solid fill based.
243
244 If combine is non-zero then alpha values will be combined.
245
246 =cut
247 */
248
249 i_fill_t *
250 i_new_fill_solid(i_color *c, int combine) {
251   int ch;
252   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t));
253
254   if (combine) {
255     *fill = base_solid_fill_comb;
256     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
257   }
258   else
259     *fill = base_solid_fill;
260   fill->c = *c;
261   for (ch = 0; ch < MAXCHANNELS; ++ch) {
262     fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
263   }
264   
265   return &fill->base;
266 }
267
268 static unsigned char
269 builtin_hatches[][8] =
270 {
271   {
272     /* 1x1 checkerboard */
273     0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
274   },
275   {
276     /* 2x2 checkerboard */
277     0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33,
278   },
279   {
280     /* 4 x 4 checkerboard */
281     0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
282   },
283   {
284     /* single vertical lines */
285     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
286   },
287   {
288     /* double vertical lines */
289     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 
290   },
291   {
292     /* quad vertical lines */
293     0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
294   },
295   {
296     /* single hlines */
297     0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298   },
299   {
300     /* double hlines */
301     0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
302   },
303   {
304     /* quad hlines */
305     0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
306   },
307   {
308     /* single / */
309     0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
310   },
311   {
312     /* single \ */
313     0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
314   },
315   {
316     /* double / */
317     0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
318   },
319   {
320     /* double \ */
321     0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11,
322   },
323   {
324     /* single grid */
325     0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
326   },
327   {
328     /* double grid */
329     0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88,
330   },
331   {
332     /* quad grid */
333     0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA,
334   },
335   {
336     /* single dots */
337     0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338   },
339   {
340     /* 4 dots */
341     0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
342   },
343   {
344     /* 16 dots */
345     0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
346   },
347   {
348     /* simple stipple */
349     0x48, 0x84, 0x00, 0x00, 0x84, 0x48, 0x00, 0x00,
350   },
351   {
352     /* weave */
353     0x55, 0xFD, 0x05, 0xFD, 0x55, 0xDF, 0x50, 0xDF,
354   },
355   {
356     /* single cross hatch */
357     0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x01,
358   },
359   {
360     /* double cross hatch */
361     0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11,
362   },
363   {
364     /* vertical lozenge */
365     0x11, 0x11, 0x11, 0xAA, 0x44, 0x44, 0x44, 0xAA,
366   },
367   {
368     /* horizontal lozenge */
369     0x88, 0x70, 0x88, 0x07, 0x88, 0x70, 0x88, 0x07,
370   },
371   {
372     /* scales overlapping downwards */
373     0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3,
374   },
375   {
376     /* scales overlapping upwards */
377     0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01,
378   },
379   {
380     /* scales overlapping leftwards */
381     0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84,
382   },
383   {
384     /* scales overlapping rightwards */
385     0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1,
386   },
387   {
388     /* denser stipple */
389     0x44, 0x88, 0x22, 0x11, 0x44, 0x88, 0x22, 0x11,
390   },
391   {
392     /* L-shaped tiles */
393     0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
394   },
395   {
396     /* wider stipple */
397     0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
398   },
399 };
400
401 typedef struct
402 {
403   i_fill_t base;
404   i_color fg, bg;
405   i_fcolor ffg, fbg;
406   unsigned char hatch[8];
407   int dx, dy;
408 } i_fill_hatch_t;
409
410 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
411                        i_color *data);
412 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
413                         i_fcolor *data);
414 static
415 i_fill_t *
416 i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, 
417                 int combine, int hatch, unsigned char *cust_hatch,
418                 int dx, int dy);
419
420 /*
421 =item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
422
423 Creates a new hatched fill with the fg color used for the 1 bits in
424 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
425 will be combined.
426
427 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
428 hash definition, with the high-bits to the left.
429
430 If cust_hatch is NULL then one of the standard hatches is used.
431
432 (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.
433
434 =cut
435 */
436 i_fill_t *
437 i_new_fill_hatch(i_color *fg, i_color *bg, int combine, int hatch, 
438             unsigned char *cust_hatch, int dx, int dy) {
439   return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, 
440                          dx, dy);
441 }
442
443 /*
444 =item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
445
446 Creates a new hatched fill with the fg color used for the 1 bits in
447 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
448 will be combined.
449
450 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
451 hash definition, with the high-bits to the left.
452
453 If cust_hatch is NULL then one of the standard hatches is used.
454
455 (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.
456
457 =cut
458 */
459 i_fill_t *
460 i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch, 
461             unsigned char *cust_hatch, int dx, int dy) {
462   return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, 
463                          dx, dy);
464 }
465
466 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
467                        i_color *data);
468 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
469                        i_fcolor *data);
470 struct i_fill_image_t {
471   i_fill_t base;
472   i_img *src;
473   int xoff, yoff;
474   int has_matrix;
475   double matrix[9];
476 };
477
478 /*
479 =item i_new_fill_image(im, matrix, xoff, yoff, combine)
480
481 Create an image based fill.
482
483 =cut
484 */
485 i_fill_t *
486 i_new_fill_image(i_img *im, double *matrix, int xoff, int yoff, int combine) {
487   struct i_fill_image_t *fill = mymalloc(sizeof(*fill));
488
489   fill->base.fill_with_color = fill_image;
490   fill->base.fill_with_fcolor = fill_imagef;
491   fill->base.destroy = NULL;
492
493   if (combine) {
494     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
495   }
496   else {
497     fill->base.combine = NULL;
498     fill->base.combinef = NULL;
499   }
500   fill->src = im;
501   if (xoff < 0)
502     xoff += im->xsize;
503   fill->xoff = xoff;
504   if (yoff < 0)
505     yoff += im->ysize;
506   fill->yoff = yoff;
507   if (matrix) {
508     fill->has_matrix = 1;
509     memcpy(fill->matrix, matrix, sizeof(fill->matrix));
510   }
511   else
512     fill->has_matrix = 0;
513
514   return &fill->base;
515 }
516
517
518 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
519
520 /*
521 =back
522
523 =head1 INTERNAL FUNCTIONS
524
525 =over
526
527 =item fill_solid(fill, x, y, width, channels, data)
528
529 The 8-bit sample fill function for non-combining solid fills.
530
531 =cut
532 */
533 static void
534 fill_solid(i_fill_t *fill, int x, int y, int width, int channels, 
535            i_color *data) {
536   while (width-- > 0) {
537     *data++ = T_SOLID_FILL(fill)->c;
538   }
539 }
540
541 /*
542 =item fill_solid(fill, x, y, width, channels, data)
543
544 The floating sample fill function for non-combining solid fills.
545
546 =cut
547 */
548 static void
549 fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, 
550            i_fcolor *data) {
551   while (width-- > 0) {
552     *data++ = T_SOLID_FILL(fill)->fc;
553   }
554 }
555
556 /*
557 =item fill_solid_comb(fill, x, y, width, channels, data)
558
559 The 8-bit sample fill function for combining solid fills.
560
561 =cut
562 */
563 static void
564 fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels, 
565                 i_color *data) {
566   i_color c = T_SOLID_FILL(fill)->c;
567
568   while (width-- > 0) {
569     *data++ = c;
570   }
571 }
572
573 /*
574 =item fill_solidf_comb(fill, x, y, width, channels, data)
575
576 The floating sample fill function for combining solid fills.
577
578 =cut
579 */
580 static void
581 fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, 
582            i_fcolor *data) {
583   i_fcolor c = T_SOLID_FILL(fill)->fc;
584
585   while (width-- > 0) {
586     *data++ = c;
587   }
588 }
589
590 /*
591 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
592
593 Implements creation of hatch fill objects.
594
595 =cut
596 */
597 static
598 i_fill_t *
599 i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, 
600                 int combine, int hatch, unsigned char *cust_hatch,
601                 int dx, int dy) {
602   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t));
603
604   fill->base.fill_with_color = fill_hatch;
605   fill->base.fill_with_fcolor = fill_hatchf;
606   fill->base.destroy = NULL;
607   fill->fg = fg ? *fg : fcolor_to_color(ffg);
608   fill->bg = bg ? *bg : fcolor_to_color(fbg);
609   fill->ffg = ffg ? *ffg : color_to_fcolor(fg);
610   fill->fbg = fbg ? *fbg : color_to_fcolor(bg);
611   if (combine) {
612     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
613   }
614   else {
615     fill->base.combine = NULL;
616     fill->base.combinef = NULL;
617   }
618   if (cust_hatch) {
619     memcpy(fill->hatch, cust_hatch, 8);
620   }
621   else {
622     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
623       hatch = 0;
624     memcpy(fill->hatch, builtin_hatches[hatch], 8);
625   }
626   fill->dx = dx & 7;
627   fill->dy = dy & 7;
628
629   return &fill->base;
630 }
631
632 /*
633 =item fill_hatch(fill, x, y, width, channels, data)
634
635 The 8-bit sample fill function for hatched fills.
636
637 =cut
638 */
639 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
640                        i_color *data) {
641   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
642   int byte = f->hatch[(y + f->dy) & 7];
643   int xpos = (x + f->dx) & 7;
644   int mask = 128 >> xpos;
645
646   while (width-- > 0) {
647     *data++ = (byte & mask) ? f->fg : f->bg;
648     
649     if ((mask >>= 1) == 0)
650       mask = 128;
651   }
652 }
653
654 /*
655 =item fill_hatchf(fill, x, y, width, channels, data)
656
657 The floating sample fill function for hatched fills.
658
659 =back
660 */
661 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
662                         i_fcolor *data) {
663   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
664   int byte = f->hatch[(y + f->dy) & 7];
665   int xpos = (x + f->dx) & 7;
666   int mask = 128 >> xpos;
667   
668   while (width-- > 0) {
669     *data++ = (byte & mask) ? f->ffg : f->fbg;
670     
671     if ((mask >>= 1) == 0)
672       mask = 128;
673   }
674 }
675
676 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
677 /* linear interpolation */
678 static i_color interp_i_color(i_color before, i_color after, double pos,
679                               int channels) {
680   i_color out;
681   int ch;
682
683   pos -= floor(pos);
684   for (ch = 0; ch < channels; ++ch)
685     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
686   if (out.channel[3])
687     for (ch = 0; ch < channels; ++ch)
688       if (ch != 3) {
689         int temp = out.channel[ch] * 255 / out.channel[3];
690         if (temp > 255)
691           temp = 255;
692         out.channel[ch] = temp;
693       }
694
695   return out;
696 }
697
698 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
699 /* linear interpolation */
700 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
701                                 int channels) {
702   i_fcolor out;
703   int ch;
704
705   pos -= floor(pos);
706   for (ch = 0; ch < channels; ++ch)
707     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
708   if (out.channel[3])
709     for (ch = 0; ch < channels; ++ch)
710       if (ch != 3) {
711         int temp = out.channel[ch] / out.channel[3];
712         if (temp > 1.0)
713           temp = 1.0;
714         out.channel[ch] = temp;
715       }
716
717   return out;
718 }
719
720 /*
721 =item fill_image(fill, x, y, width, channels, data, work)
722
723 =cut
724 */
725 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
726                        i_color *data) {
727   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
728   int i = 0;
729   i_color c;
730   
731   if (f->has_matrix) {
732     /* the hard way */
733     while (i < width) {
734       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
735       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
736       double ix = floor(rx / f->src->xsize);
737       double iy = floor(ry / f->src->ysize);
738       i_color c[2][2];
739       i_color c2[2];
740       int dy;
741
742       if (f->xoff) {
743         rx += iy * f->xoff;
744         ix = floor(rx / f->src->xsize);
745       }
746       else if (f->yoff) {
747         ry += ix * f->yoff;
748         iy = floor(ry / f->src->ysize);
749       }
750       rx -= ix * f->src->xsize;
751       ry -= iy * f->src->ysize;
752
753       for (dy = 0; dy < 2; ++dy) {
754         if ((int)rx == f->src->xsize-1) {
755           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
756           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
757         }
758         else {
759           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
760                  c[dy]);
761         }
762         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
763       }
764       *data++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
765       ++i;
766     }
767   }
768   else {
769     /* the easy way */
770     /* this should be possible to optimize to use i_glin() */
771     while (i < width) {
772       int rx = x+i;
773       int ry = y;
774       int ix = rx / f->src->xsize;
775       int iy = ry / f->src->ysize;
776
777       if (f->xoff) {
778         rx += iy * f->xoff;
779         ix = rx / f->src->xsize;
780       }
781       else if (f->yoff) {
782         ry += ix * f->yoff;
783         iy = ry / f->src->xsize;
784       }
785       rx -= ix * f->src->xsize;
786       ry -= iy * f->src->ysize;
787       i_gpix(f->src, rx, ry, data);
788       ++data;
789       ++i;
790     }
791   }
792 }
793
794 /*
795 =item fill_image(fill, x, y, width, channels, data, work)
796
797 =cut
798 */
799 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
800                        i_fcolor *data) {
801   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
802   int i = 0;
803   i_fcolor c;
804   
805   if (f->has_matrix) {
806     /* the hard way */
807     while (i < width) {
808       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
809       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
810       double ix = floor(rx / f->src->xsize);
811       double iy = floor(ry / f->src->ysize);
812       i_fcolor c[2][2];
813       i_fcolor c2[2];
814       int dy;
815
816       if (f->xoff) {
817         rx += iy * f->xoff;
818         ix = floor(rx / f->src->xsize);
819       }
820       else if (f->yoff) {
821         ry += ix * f->yoff;
822         iy = floor(ry / f->src->ysize);
823       }
824       rx -= ix * f->src->xsize;
825       ry -= iy * f->src->ysize;
826
827       for (dy = 0; dy < 2; ++dy) {
828         if ((int)rx == f->src->xsize-1) {
829           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
830           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
831         }
832         else {
833           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
834                  c[dy]);
835         }
836         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
837       }
838       *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
839       ++i;
840     }
841   }
842   else {
843     /* the easy way */
844     /* this should be possible to optimize to use i_glin() */
845     while (i < width) {
846       int rx = x+i;
847       int ry = y;
848       int ix = rx / f->src->xsize;
849       int iy = ry / f->src->ysize;
850
851       if (f->xoff) {
852         rx += iy * f->xoff;
853         ix = rx / f->src->xsize;
854       }
855       else if (f->yoff) {
856         ry += ix * f->yoff;
857         iy = ry / f->src->xsize;
858       }
859       rx -= ix * f->src->xsize;
860       ry -= iy * f->src->ysize;
861       i_gpixf(f->src, rx, ry, data);
862       ++data;
863       ++i;
864     }
865   }
866 }
867
868 static void combine_replace(i_color *, i_color *, int, int);
869 static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
870 static void combine_alphablend(i_color *, i_color *, int, int);
871 static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
872 static void combine_mult(i_color *, i_color *, int, int);
873 static void combine_multf(i_fcolor *, i_fcolor *, int, int);
874 static void combine_dissolve(i_color *, i_color *, int, int);
875 static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
876 static void combine_add(i_color *, i_color *, int, int);
877 static void combine_addf(i_fcolor *, i_fcolor *, int, int);
878 static void combine_subtract(i_color *, i_color *, int, int);
879 static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
880 static void combine_diff(i_color *, i_color *, int, int);
881 static void combine_difff(i_fcolor *, i_fcolor *, int, int);
882 static void combine_darken(i_color *, i_color *, int, int);
883 static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
884 static void combine_lighten(i_color *, i_color *, int, int);
885 static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
886 static void combine_hue(i_color *, i_color *, int, int);
887 static void combine_huef(i_fcolor *, i_fcolor *, int, int);
888 static void combine_sat(i_color *, i_color *, int, int);
889 static void combine_satf(i_fcolor *, i_fcolor *, int, int);
890 static void combine_value(i_color *, i_color *, int, int);
891 static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
892 static void combine_color(i_color *, i_color *, int, int);
893 static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
894
895 struct i_combines {
896   i_fill_combine_f combine;
897   i_fill_combinef_f combinef;
898 } combines[] =
899 {
900   { /* replace */
901     combine_replace,
902     combine_replacef,
903   },
904   { /* alpha blend */
905     combine_alphablend,
906     combine_alphablendf,
907   },
908   {
909     /* multiply */
910     combine_mult,
911     combine_multf,
912   },
913   {
914     /* dissolve */
915     combine_dissolve,
916     combine_dissolvef,
917   },
918   {
919     /* add */
920     combine_add,
921     combine_addf,
922   },
923   {
924     /* subtract */
925     combine_subtract,
926     combine_subtractf,
927   },
928   {
929     /* diff */
930     combine_diff,
931     combine_difff,
932   },
933   {
934     combine_lighten,
935     combine_lightenf,
936   },
937   {
938     combine_darken,
939     combine_darkenf,
940   },
941   {
942     combine_hue,
943     combine_huef,
944   },
945   {
946     combine_sat,
947     combine_satf,
948   },
949   {
950     combine_value,
951     combine_valuef,
952   },
953   {
954     combine_color,
955     combine_colorf,
956   },
957 };
958
959 /*
960 =item i_get_combine(combine, color_func, fcolor_func)
961
962 =cut
963 */
964
965 void i_get_combine(int combine, i_fill_combine_f *color_func, 
966                    i_fill_combinef_f *fcolor_func) {
967   if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
968     combine = 0;
969
970   *color_func = combines[combine].combine;
971   *fcolor_func = combines[combine].combinef;
972 }
973
974 static void combine_replace(i_color *out, i_color *in, int channels, int count) {
975   while (count--) {
976     *out++ = *in++;
977   }
978 }
979
980 static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
981   while (count--) {
982     *out++ = *in++;
983   }
984 }
985
986 static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
987   while (count--) {
988     COMBINE(*out, *in, channels);
989     ++out;
990     ++in;
991   }
992 }
993
994 static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
995   while (count--) {
996     COMBINEF(*out, *in, channels);
997     ++out;
998     ++in;
999   }
1000 }
1001
1002 static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1003   int ch;
1004
1005   while (count--) {
1006     i_color c = *in;
1007     double mult[MAXCHANNELS];
1008     mult[3] = in->channel[3];
1009     for (ch = 0; ch < (channels); ++ch) { 
1010       if (ch != 3)
1011         mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
1012     } 
1013     COMBINEA(*out, mult, channels);
1014     ++out;
1015     ++in;
1016   }
1017 }
1018
1019 static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1020   int ch;
1021
1022   while (count--) {
1023     i_fcolor c = *in;
1024     for (ch = 0; ch < channels; ++ch) { 
1025       if (ch != 3)
1026         c.channel[ch] = out->channel[ch] * in->channel[ch];
1027     } 
1028     COMBINEF(*out, c, channels);
1029     ++out;
1030     ++in;
1031   }
1032 }
1033
1034 static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
1035   int ch;
1036
1037   while (count--) {
1038     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1039       COMBINE(*out, *in, channels);
1040     ++out;
1041     ++in;
1042   }
1043 }
1044
1045 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1046   int ch;
1047
1048   while (count--) {
1049     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1050       COMBINEF(*out, *in, channels);
1051     ++out;
1052     ++in;
1053   }
1054 }
1055
1056 static void combine_add(i_color *out, i_color *in, int channels, int count) {
1057   int ch;
1058
1059   while (count--) {
1060     i_color c = *in;
1061     for (ch = 0; ch < (channels); ++ch) { 
1062       if (ch != 3) {
1063         int total = out->channel[ch] + in->channel[ch];
1064         if (total > 255)
1065           total = 255;
1066         c.channel[ch] = total;
1067       }
1068     } 
1069     COMBINE(*out, c, channels);
1070     ++out;
1071     ++in;
1072   }
1073 }
1074
1075 static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1076   int ch;
1077
1078   while (count--) {
1079     i_fcolor c = *in;
1080     for (ch = 0; ch < (channels); ++ch) { 
1081       if (ch != 3) {
1082         double total = out->channel[ch] + in->channel[ch];
1083         if (total > 1.0)
1084           total = 1.0;
1085         out->channel[ch] = total;
1086       }
1087     } 
1088     COMBINEF(*out, c, channels);
1089     ++out;
1090     ++in;
1091   }
1092 }
1093
1094 static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
1095   int ch;
1096
1097   while (count--) {
1098     i_color c = *in;
1099     for (ch = 0; ch < (channels); ++ch) { 
1100       if (ch != 3) {
1101         int total = out->channel[ch] - in->channel[ch];
1102         if (total < 0)
1103           total = 0;
1104         c.channel[ch] = total;
1105       }
1106     } 
1107     COMBINE(*out, c, channels);
1108     ++out;
1109     ++in;
1110   }
1111 }
1112
1113 static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1114   int ch;
1115
1116   while (count--) {
1117     i_fcolor c = *in;
1118     for (ch = 0; ch < channels; ++ch) { 
1119       if (ch != 3) {
1120         double total = out->channel[ch] - in->channel[ch];
1121         if (total < 0)
1122           total = 0;
1123         c.channel[ch] = total;
1124       }
1125     } 
1126     COMBINEF(*out, c, channels);
1127     ++out;
1128     ++in;
1129   }
1130 }
1131
1132 static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1133   int ch;
1134
1135   while (count--) {
1136     i_color c = *in;
1137     for (ch = 0; ch < (channels); ++ch) { 
1138       if (ch != 3) 
1139         c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1140     } 
1141     COMBINE(*out, c, channels)
1142     ++out;
1143     ++in;
1144   }
1145 }
1146
1147 static void combine_difff(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         c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
1155     }
1156     COMBINEF(*out, c, channels);
1157     ++out;
1158     ++in;
1159   }
1160 }
1161
1162 static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1163   int ch;
1164
1165   while (count--) {
1166     for (ch = 0; ch < channels; ++ch) { 
1167       if (ch != 3 && out->channel[ch] < in->channel[ch])
1168         in->channel[ch] = out->channel[ch];
1169     } 
1170     COMBINE(*out, *in, channels);
1171     ++out;
1172     ++in;
1173   }
1174 }
1175
1176 static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1177   int ch;
1178
1179   while (count--) {
1180     for (ch = 0; ch < channels; ++ch) { 
1181       if (ch != 3 && out->channel[ch] < in->channel[ch])
1182         in->channel[ch] = out->channel[ch];
1183     } 
1184     COMBINEF(*out, *in, channels);
1185     ++out;
1186     ++in;
1187   }
1188 }
1189
1190 static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1191   int ch;
1192
1193   while (count--) {
1194     for (ch = 0; ch < channels; ++ch) { 
1195       if (ch != 3 && out->channel[ch] > in->channel[ch])
1196         in->channel[ch] = out->channel[ch];
1197     } 
1198     COMBINE(*out, *in, channels);
1199     ++out;
1200     ++in;
1201   }
1202 }
1203
1204 static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1205   int ch;
1206
1207   while (count--) {
1208     for (ch = 0; ch < channels; ++ch) { 
1209       if (ch != 3 && out->channel[ch] > in->channel[ch])
1210         in->channel[ch] = out->channel[ch];
1211     } 
1212     COMBINEF(*out, *in, channels);
1213     ++out;
1214     ++in;
1215   }
1216 }
1217
1218 static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1219   while (count--) {
1220     i_color c = *out;
1221     i_rgb_to_hsv(&c);
1222     i_rgb_to_hsv(in);
1223     c.channel[0] = in->channel[0];
1224     i_hsv_to_rgb(&c);
1225     c.channel[3] = in->channel[3];
1226     COMBINE(*out, c, channels);
1227     ++out;
1228     ++in;
1229   }
1230 }
1231
1232 static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1233   while (count--) {
1234     i_fcolor c = *out;
1235     i_rgb_to_hsvf(&c);
1236     i_rgb_to_hsvf(in);
1237     c.channel[0] = in->channel[0];
1238     i_hsv_to_rgbf(&c);
1239     c.channel[3] = in->channel[3];
1240     COMBINEF(*out, c, channels);
1241     ++out;
1242     ++in;
1243   }
1244 }
1245
1246 static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1247   while (count--) {
1248     i_color c = *out;
1249     i_rgb_to_hsv(&c);
1250     i_rgb_to_hsv(in);
1251     c.channel[1] = in->channel[1];
1252     i_hsv_to_rgb(&c);
1253     c.channel[3] = in->channel[3];
1254     COMBINE(*out, c, channels);
1255     ++out;
1256     ++in;
1257   }
1258 }
1259
1260 static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1261   while (count--) {
1262     i_fcolor c = *out;
1263     i_rgb_to_hsvf(&c);
1264     i_rgb_to_hsvf(in);
1265     c.channel[1] = in->channel[1];
1266     i_hsv_to_rgbf(&c);
1267     c.channel[3] = in->channel[3];
1268     COMBINEF(*out, c, channels);
1269     ++out;
1270     ++in;
1271   }
1272 }
1273
1274 static void combine_value(i_color *out, i_color *in, int channels, int count) {
1275   while (count--) {
1276     i_color c = *out;
1277     i_rgb_to_hsv(&c);
1278     i_rgb_to_hsv(in);
1279     c.channel[2] = in->channel[2];
1280     i_hsv_to_rgb(&c);
1281     c.channel[3] = in->channel[3];
1282     COMBINE(*out, c, channels);
1283     ++out;
1284     ++in;
1285   }
1286 }
1287
1288 static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
1289                            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[2] = in->channel[2];
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_color(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[0] = in->channel[0];
1309     c.channel[1] = in->channel[1];
1310     i_hsv_to_rgb(&c);
1311     c.channel[3] = in->channel[3];
1312     COMBINE(*out, c, channels);
1313     ++out;
1314     ++in;
1315   }
1316 }
1317
1318 static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
1319                            int count) {
1320   while (count--) {
1321     i_fcolor c = *out;
1322     i_rgb_to_hsvf(&c);
1323     i_rgb_to_hsvf(in);
1324     c.channel[0] = in->channel[0];
1325     c.channel[1] = in->channel[1];
1326     i_hsv_to_rgbf(&c);
1327     c.channel[3] = in->channel[3];
1328     COMBINEF(*out, c, channels);
1329     ++out;
1330     ++in;
1331   }
1332 }
1333
1334
1335 /*
1336 =back
1337
1338 =head1 AUTHOR
1339
1340 Tony Cook <tony@develop-help.com>
1341
1342 =head1 SEE ALSO
1343
1344 Imager(3)
1345
1346 =cut
1347 */