]> git.imager.perl.org - imager.git/blob - fills.c
- add tests to exercise hatch fills for floating images. This code is
[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 *out = data;
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       *out++ = 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, out);
788       ++out;
789       ++i;
790     }
791   }
792   if (f->src->channels == 3) {
793     /* just set the alpha */
794     for (i = 0; i <  width; ++i) {
795       data->channel[3] = 255;
796       data++;
797     }
798   }
799   else if (f->src->channels == 2) {
800     /* copy the alpha to channel 3, duplicate the grey value */
801     for (i = 0; i <  width; ++i) {
802       data->channel[3] = data->channel[1];
803       data->channel[1] = data->channel[2] = data->channel[0];
804       data++;
805     }
806   }
807   else if (f->src->channels == 1) {
808     /* set the alpha, duplicate grey */
809     for (i = 0; i <  width; ++i) {
810       data->channel[3] = 255;
811       data->channel[1] = data->channel[2] = data->channel[0];
812       data++;
813     }
814   }
815 }
816
817 /*
818 =item fill_image(fill, x, y, width, channels, data, work)
819
820 =cut
821 */
822 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
823                        i_fcolor *data) {
824   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
825   int i = 0;
826   
827   if (f->has_matrix) {
828     /* the hard way */
829     while (i < width) {
830       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
831       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
832       double ix = floor(rx / f->src->xsize);
833       double iy = floor(ry / f->src->ysize);
834       i_fcolor c[2][2];
835       i_fcolor c2[2];
836       int dy;
837
838       if (f->xoff) {
839         rx += iy * f->xoff;
840         ix = floor(rx / f->src->xsize);
841       }
842       else if (f->yoff) {
843         ry += ix * f->yoff;
844         iy = floor(ry / f->src->ysize);
845       }
846       rx -= ix * f->src->xsize;
847       ry -= iy * f->src->ysize;
848
849       for (dy = 0; dy < 2; ++dy) {
850         if ((int)rx == f->src->xsize-1) {
851           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
852           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
853         }
854         else {
855           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
856                  c[dy]);
857         }
858         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
859       }
860       *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
861       ++i;
862     }
863   }
864   else {
865     /* the easy way */
866     /* this should be possible to optimize to use i_glin() */
867     while (i < width) {
868       int rx = x+i;
869       int ry = y;
870       int ix = rx / f->src->xsize;
871       int iy = ry / f->src->ysize;
872
873       if (f->xoff) {
874         rx += iy * f->xoff;
875         ix = rx / f->src->xsize;
876       }
877       else if (f->yoff) {
878         ry += ix * f->yoff;
879         iy = ry / f->src->xsize;
880       }
881       rx -= ix * f->src->xsize;
882       ry -= iy * f->src->ysize;
883       i_gpixf(f->src, rx, ry, data);
884       ++data;
885       ++i;
886     }
887   }
888   if (f->src->channels == 3) {
889     /* just set the alpha */
890     for (i = 0; i <  width; ++i) {
891       data->channel[3] = 1.0;
892       data++;
893     }
894   }
895   else if (f->src->channels == 2) {
896     /* copy the alpha to channel 3, duplicate the grey value */
897     for (i = 0; i <  width; ++i) {
898       data->channel[3] = data->channel[1];
899       data->channel[1] = data->channel[2] = data->channel[0];
900       data++;
901     }
902   }
903   else if (f->src->channels == 1) {
904     /* set the alpha, duplicate grey */
905     for (i = 0; i <  width; ++i) {
906       data->channel[3] = 1.0;
907       data->channel[1] = data->channel[2] = data->channel[0];
908       data++;
909     }
910   }
911 }
912
913 static void combine_replace(i_color *, i_color *, int, int);
914 static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
915 static void combine_alphablend(i_color *, i_color *, int, int);
916 static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
917 static void combine_mult(i_color *, i_color *, int, int);
918 static void combine_multf(i_fcolor *, i_fcolor *, int, int);
919 static void combine_dissolve(i_color *, i_color *, int, int);
920 static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
921 static void combine_add(i_color *, i_color *, int, int);
922 static void combine_addf(i_fcolor *, i_fcolor *, int, int);
923 static void combine_subtract(i_color *, i_color *, int, int);
924 static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
925 static void combine_diff(i_color *, i_color *, int, int);
926 static void combine_difff(i_fcolor *, i_fcolor *, int, int);
927 static void combine_darken(i_color *, i_color *, int, int);
928 static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
929 static void combine_lighten(i_color *, i_color *, int, int);
930 static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
931 static void combine_hue(i_color *, i_color *, int, int);
932 static void combine_huef(i_fcolor *, i_fcolor *, int, int);
933 static void combine_sat(i_color *, i_color *, int, int);
934 static void combine_satf(i_fcolor *, i_fcolor *, int, int);
935 static void combine_value(i_color *, i_color *, int, int);
936 static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
937 static void combine_color(i_color *, i_color *, int, int);
938 static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
939
940 static struct i_combines {
941   i_fill_combine_f combine;
942   i_fill_combinef_f combinef;
943 } combines[] =
944 {
945   { /* replace */
946     combine_replace,
947     combine_replacef,
948   },
949   { /* alpha blend */
950     combine_alphablend,
951     combine_alphablendf,
952   },
953   {
954     /* multiply */
955     combine_mult,
956     combine_multf,
957   },
958   {
959     /* dissolve */
960     combine_dissolve,
961     combine_dissolvef,
962   },
963   {
964     /* add */
965     combine_add,
966     combine_addf,
967   },
968   {
969     /* subtract */
970     combine_subtract,
971     combine_subtractf,
972   },
973   {
974     /* diff */
975     combine_diff,
976     combine_difff,
977   },
978   {
979     combine_lighten,
980     combine_lightenf,
981   },
982   {
983     combine_darken,
984     combine_darkenf,
985   },
986   {
987     combine_hue,
988     combine_huef,
989   },
990   {
991     combine_sat,
992     combine_satf,
993   },
994   {
995     combine_value,
996     combine_valuef,
997   },
998   {
999     combine_color,
1000     combine_colorf,
1001   },
1002 };
1003
1004 /*
1005 =item i_get_combine(combine, color_func, fcolor_func)
1006
1007 =cut
1008 */
1009
1010 void i_get_combine(int combine, i_fill_combine_f *color_func, 
1011                    i_fill_combinef_f *fcolor_func) {
1012   if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
1013     combine = 0;
1014
1015   *color_func = combines[combine].combine;
1016   *fcolor_func = combines[combine].combinef;
1017 }
1018
1019 static void combine_replace(i_color *out, i_color *in, int channels, int count) {
1020   while (count--) {
1021     *out++ = *in++;
1022   }
1023 }
1024
1025 static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1026   while (count--) {
1027     *out++ = *in++;
1028   }
1029 }
1030
1031 static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
1032   while (count--) {
1033     COMBINE(*out, *in, channels);
1034     ++out;
1035     ++in;
1036   }
1037 }
1038
1039 static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1040   while (count--) {
1041     COMBINEF(*out, *in, channels);
1042     ++out;
1043     ++in;
1044   }
1045 }
1046
1047 static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1048   int ch;
1049
1050   while (count--) {
1051     double mult[MAXCHANNELS];
1052     mult[3] = in->channel[3];
1053     for (ch = 0; ch < (channels); ++ch) { 
1054       if (ch != 3)
1055         mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
1056     } 
1057     COMBINEA(*out, mult, channels);
1058     ++out;
1059     ++in;
1060   }
1061 }
1062
1063 static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1064   int ch;
1065
1066   while (count--) {
1067     i_fcolor c = *in;
1068     for (ch = 0; ch < channels; ++ch) { 
1069       if (ch != 3)
1070         c.channel[ch] = out->channel[ch] * in->channel[ch];
1071     } 
1072     COMBINEF(*out, c, channels);
1073     ++out;
1074     ++in;
1075   }
1076 }
1077
1078 static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
1079   while (count--) {
1080     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1081       COMBINE(*out, *in, channels);
1082     ++out;
1083     ++in;
1084   }
1085 }
1086
1087 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1088   while (count--) {
1089     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1090       COMBINEF(*out, *in, channels);
1091     ++out;
1092     ++in;
1093   }
1094 }
1095
1096 static void combine_add(i_color *out, i_color *in, int channels, int count) {
1097   int ch;
1098
1099   while (count--) {
1100     i_color c = *in;
1101     for (ch = 0; ch < (channels); ++ch) { 
1102       if (ch != 3) {
1103         int total = out->channel[ch] + in->channel[ch];
1104         if (total > 255)
1105           total = 255;
1106         c.channel[ch] = total;
1107       }
1108     } 
1109     COMBINE(*out, c, channels);
1110     ++out;
1111     ++in;
1112   }
1113 }
1114
1115 static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1116   int ch;
1117
1118   while (count--) {
1119     i_fcolor c = *in;
1120     for (ch = 0; ch < (channels); ++ch) { 
1121       if (ch != 3) {
1122         double total = out->channel[ch] + in->channel[ch];
1123         if (total > 1.0)
1124           total = 1.0;
1125         out->channel[ch] = total;
1126       }
1127     } 
1128     COMBINEF(*out, c, channels);
1129     ++out;
1130     ++in;
1131   }
1132 }
1133
1134 static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
1135   int ch;
1136
1137   while (count--) {
1138     i_color c = *in;
1139     for (ch = 0; ch < (channels); ++ch) { 
1140       if (ch != 3) {
1141         int total = out->channel[ch] - in->channel[ch];
1142         if (total < 0)
1143           total = 0;
1144         c.channel[ch] = total;
1145       }
1146     } 
1147     COMBINE(*out, c, channels);
1148     ++out;
1149     ++in;
1150   }
1151 }
1152
1153 static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1154   int ch;
1155
1156   while (count--) {
1157     i_fcolor c = *in;
1158     for (ch = 0; ch < channels; ++ch) { 
1159       if (ch != 3) {
1160         double total = out->channel[ch] - in->channel[ch];
1161         if (total < 0)
1162           total = 0;
1163         c.channel[ch] = total;
1164       }
1165     } 
1166     COMBINEF(*out, c, channels);
1167     ++out;
1168     ++in;
1169   }
1170 }
1171
1172 static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1173   int ch;
1174
1175   while (count--) {
1176     i_color c = *in;
1177     for (ch = 0; ch < (channels); ++ch) { 
1178       if (ch != 3) 
1179         c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1180     } 
1181     COMBINE(*out, c, channels)
1182     ++out;
1183     ++in;
1184   }
1185 }
1186
1187 static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
1188   int ch;
1189
1190   while (count--) {
1191     i_fcolor c = *in;
1192     for (ch = 0; ch < (channels); ++ch) { 
1193       if (ch != 3)
1194         c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
1195     }
1196     COMBINEF(*out, c, channels);
1197     ++out;
1198     ++in;
1199   }
1200 }
1201
1202 static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1203   int ch;
1204
1205   while (count--) {
1206     for (ch = 0; ch < channels; ++ch) { 
1207       if (ch != 3 && out->channel[ch] < in->channel[ch])
1208         in->channel[ch] = out->channel[ch];
1209     } 
1210     COMBINE(*out, *in, channels);
1211     ++out;
1212     ++in;
1213   }
1214 }
1215
1216 static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1217   int ch;
1218
1219   while (count--) {
1220     for (ch = 0; ch < channels; ++ch) { 
1221       if (ch != 3 && out->channel[ch] < in->channel[ch])
1222         in->channel[ch] = out->channel[ch];
1223     } 
1224     COMBINEF(*out, *in, channels);
1225     ++out;
1226     ++in;
1227   }
1228 }
1229
1230 static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1231   int ch;
1232
1233   while (count--) {
1234     for (ch = 0; ch < channels; ++ch) { 
1235       if (ch != 3 && out->channel[ch] > in->channel[ch])
1236         in->channel[ch] = out->channel[ch];
1237     } 
1238     COMBINE(*out, *in, channels);
1239     ++out;
1240     ++in;
1241   }
1242 }
1243
1244 static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1245   int ch;
1246
1247   while (count--) {
1248     for (ch = 0; ch < channels; ++ch) { 
1249       if (ch != 3 && out->channel[ch] > in->channel[ch])
1250         in->channel[ch] = out->channel[ch];
1251     } 
1252     COMBINEF(*out, *in, channels);
1253     ++out;
1254     ++in;
1255   }
1256 }
1257
1258 static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1259   while (count--) {
1260     i_color c = *out;
1261     i_rgb_to_hsv(&c);
1262     i_rgb_to_hsv(in);
1263     c.channel[0] = in->channel[0];
1264     i_hsv_to_rgb(&c);
1265     c.channel[3] = in->channel[3];
1266     COMBINE(*out, c, channels);
1267     ++out;
1268     ++in;
1269   }
1270 }
1271
1272 static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1273   while (count--) {
1274     i_fcolor c = *out;
1275     i_rgb_to_hsvf(&c);
1276     i_rgb_to_hsvf(in);
1277     c.channel[0] = in->channel[0];
1278     i_hsv_to_rgbf(&c);
1279     c.channel[3] = in->channel[3];
1280     COMBINEF(*out, c, channels);
1281     ++out;
1282     ++in;
1283   }
1284 }
1285
1286 static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1287   while (count--) {
1288     i_color c = *out;
1289     i_rgb_to_hsv(&c);
1290     i_rgb_to_hsv(in);
1291     c.channel[1] = in->channel[1];
1292     i_hsv_to_rgb(&c);
1293     c.channel[3] = in->channel[3];
1294     COMBINE(*out, c, channels);
1295     ++out;
1296     ++in;
1297   }
1298 }
1299
1300 static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1301   while (count--) {
1302     i_fcolor c = *out;
1303     i_rgb_to_hsvf(&c);
1304     i_rgb_to_hsvf(in);
1305     c.channel[1] = in->channel[1];
1306     i_hsv_to_rgbf(&c);
1307     c.channel[3] = in->channel[3];
1308     COMBINEF(*out, c, channels);
1309     ++out;
1310     ++in;
1311   }
1312 }
1313
1314 static void combine_value(i_color *out, i_color *in, int channels, int count) {
1315   while (count--) {
1316     i_color c = *out;
1317     i_rgb_to_hsv(&c);
1318     i_rgb_to_hsv(in);
1319     c.channel[2] = in->channel[2];
1320     i_hsv_to_rgb(&c);
1321     c.channel[3] = in->channel[3];
1322     COMBINE(*out, c, channels);
1323     ++out;
1324     ++in;
1325   }
1326 }
1327
1328 static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
1329                            int count) {
1330   while (count--) {
1331     i_fcolor c = *out;
1332     i_rgb_to_hsvf(&c);
1333     i_rgb_to_hsvf(in);
1334     c.channel[2] = in->channel[2];
1335     i_hsv_to_rgbf(&c);
1336     c.channel[3] = in->channel[3];
1337     COMBINEF(*out, c, channels);
1338     ++out;
1339     ++in;
1340   }
1341 }
1342
1343 static void combine_color(i_color *out, i_color *in, int channels, int count) {
1344   while (count--) {
1345     i_color c = *out;
1346     i_rgb_to_hsv(&c);
1347     i_rgb_to_hsv(in);
1348     c.channel[0] = in->channel[0];
1349     c.channel[1] = in->channel[1];
1350     i_hsv_to_rgb(&c);
1351     c.channel[3] = in->channel[3];
1352     COMBINE(*out, c, channels);
1353     ++out;
1354     ++in;
1355   }
1356 }
1357
1358 static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
1359                            int count) {
1360   while (count--) {
1361     i_fcolor c = *out;
1362     i_rgb_to_hsvf(&c);
1363     i_rgb_to_hsvf(in);
1364     c.channel[0] = in->channel[0];
1365     c.channel[1] = in->channel[1];
1366     i_hsv_to_rgbf(&c);
1367     c.channel[3] = in->channel[3];
1368     COMBINEF(*out, c, channels);
1369     ++out;
1370     ++in;
1371   }
1372 }
1373
1374
1375 /*
1376 =back
1377
1378 =head1 AUTHOR
1379
1380 Tony Cook <tony@develop-help.com>
1381
1382 =head1 SEE ALSO
1383
1384 Imager(3)
1385
1386 =cut
1387 */