]> git.imager.perl.org - imager.git/blob - fills.c
tests missing from MANIFEST
[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 *, i_color *);
167 static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, 
168                         i_fcolor *, i_fcolor *);
169 static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels, 
170                             i_color *, i_color *);
171 static void fill_solidf_comb(i_fill_t *, int x, int y, int width, 
172                              int channels, i_fcolor *, 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, i_color *work);
412 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
413                         i_fcolor *data, i_fcolor *work);
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, i_color *work);
468 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
469                        i_fcolor *data, i_fcolor *work);
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, i_color *work) {
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, i_fcolor *work) {
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, i_color *work) {
566   i_color c = T_SOLID_FILL(fill)->c;
567   int count = width;
568   i_color *wstart = work;
569
570   while (width-- > 0) {
571     *work++ = c;
572   }
573   (fill->combine)(data, wstart, channels, count);
574 }
575
576 /*
577 =item fill_solidf_comb(fill, x, y, width, channels, data)
578
579 The floating sample fill function for combining solid fills.
580
581 =cut
582 */
583 static void
584 fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels, 
585            i_fcolor *data, i_fcolor *work) {
586   i_fcolor c = T_SOLID_FILL(fill)->fc;
587   int count = width;
588   i_fcolor *wstart = work;
589
590   while (width-- > 0) {
591     *work++ = c;
592   }
593   (fill->combinef)(data, wstart, channels, count);
594 }
595
596 /*
597 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
598
599 Implements creation of hatch fill objects.
600
601 =cut
602 */
603 static
604 i_fill_t *
605 i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, 
606                 int combine, int hatch, unsigned char *cust_hatch,
607                 int dx, int dy) {
608   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t));
609
610   fill->base.fill_with_color = fill_hatch;
611   fill->base.fill_with_fcolor = fill_hatchf;
612   fill->base.destroy = NULL;
613   fill->fg = fg ? *fg : fcolor_to_color(ffg);
614   fill->bg = bg ? *bg : fcolor_to_color(fbg);
615   fill->ffg = ffg ? *ffg : color_to_fcolor(fg);
616   fill->fbg = fbg ? *fbg : color_to_fcolor(bg);
617   if (combine) {
618     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
619   }
620   else {
621     fill->base.combine = NULL;
622     fill->base.combinef = NULL;
623   }
624   if (cust_hatch) {
625     memcpy(fill->hatch, cust_hatch, 8);
626   }
627   else {
628     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
629       hatch = 0;
630     memcpy(fill->hatch, builtin_hatches[hatch], 8);
631   }
632   fill->dx = dx & 7;
633   fill->dy = dy & 7;
634
635   return &fill->base;
636 }
637
638 /*
639 =item fill_hatch(fill, x, y, width, channels, data)
640
641 The 8-bit sample fill function for hatched fills.
642
643 =back
644 */
645 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
646                        i_color *data, i_color *work) {
647   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
648   int byte = f->hatch[(y + f->dy) & 7];
649   int xpos = (x + f->dx) & 7;
650   int mask = 128 >> xpos;
651
652   if (fill->combine) {
653     int count = width;
654     i_color *wstart = work;
655
656     while (count-- > 0) {
657       *work++ = (byte & mask) ? f->fg : f->bg;
658       
659       if ((mask >>= 1) == 0)
660         mask = 128;
661     }
662     (fill->combine)(data, wstart, channels, width);
663   }
664   else {
665     while (width-- > 0) {
666       *data++ = (byte & mask) ? f->fg : f->bg;
667
668       if ((mask >>= 1) == 0)
669         mask = 128;
670     }
671   }
672 }
673
674 /*
675 =item fill_hatchf(fill, x, y, width, channels, data)
676
677 The floating sample fill function for hatched fills.
678
679 =back
680 */
681 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
682                         i_fcolor *data, i_fcolor *work) {
683   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
684   int byte = f->hatch[(y + f->dy) & 7];
685   int xpos = (x + f->dx) & 7;
686   int mask = 128 >> xpos;
687   
688   if (fill->combinef) {
689     int count = width;
690     i_fcolor *wstart = work;
691
692     while (count-- > 0) {
693       *work++ = (byte & mask) ? f->ffg : f->fbg;
694       
695       if ((mask >>= 1) == 0)
696         mask = 128;
697     }
698     (fill->combinef)(data, wstart, channels, width);
699   }
700   else {
701     while (width-- > 0) {
702       *data++ = (byte & mask) ? f->ffg : f->fbg;
703
704       if ((mask >>= 1) == 0)
705         mask = 128;
706     }
707   }
708 }
709
710 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
711 /* linear interpolation */
712 static i_color interp_i_color(i_color before, i_color after, double pos,
713                               int channels) {
714   i_color out;
715   int ch;
716
717   pos -= floor(pos);
718   for (ch = 0; ch < channels; ++ch)
719     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
720   if (out.channel[3])
721     for (ch = 0; ch < channels; ++ch)
722       if (ch != 3) {
723         int temp = out.channel[ch] * 255 / out.channel[3];
724         if (temp > 255)
725           temp = 255;
726         out.channel[ch] = temp;
727       }
728
729   return out;
730 }
731
732 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
733 /* linear interpolation */
734 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
735                                 int channels) {
736   i_fcolor out;
737   int ch;
738
739   pos -= floor(pos);
740   for (ch = 0; ch < channels; ++ch)
741     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
742   if (out.channel[3])
743     for (ch = 0; ch < channels; ++ch)
744       if (ch != 3) {
745         int temp = out.channel[ch] / out.channel[3];
746         if (temp > 1.0)
747           temp = 1.0;
748         out.channel[ch] = temp;
749       }
750
751   return out;
752 }
753
754 /*
755 =item fill_image(fill, x, y, width, channels, data, work)
756
757 =cut
758 */
759 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
760                        i_color *data, i_color *work) {
761   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
762   i_color *out = fill->combine ? work : data;
763   int i = 0;
764   i_color c;
765   
766   if (f->has_matrix) {
767     /* the hard way */
768     while (i < width) {
769       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
770       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
771       double ix = floor(rx / f->src->xsize);
772       double iy = floor(ry / f->src->ysize);
773       i_color c[2][2];
774       i_color c2[2];
775       int dy;
776
777       if (f->xoff) {
778         rx += iy * f->xoff;
779         ix = floor(rx / f->src->xsize);
780       }
781       else if (f->yoff) {
782         ry += ix * f->yoff;
783         iy = floor(ry / f->src->ysize);
784       }
785       rx -= ix * f->src->xsize;
786       ry -= iy * f->src->ysize;
787
788       for (dy = 0; dy < 2; ++dy) {
789         if ((int)rx == f->src->xsize-1) {
790           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
791           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
792         }
793         else {
794           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
795                  c[dy]);
796         }
797         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
798       }
799       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
800       ++i;
801     }
802   }
803   else {
804     /* the easy way */
805     /* this should be possible to optimize to use i_glin() */
806     while (i < width) {
807       int rx = x+i;
808       int ry = y;
809       int ix = rx / f->src->xsize;
810       int iy = ry / f->src->ysize;
811
812       if (f->xoff) {
813         rx += iy * f->xoff;
814         ix = rx / f->src->xsize;
815       }
816       else if (f->yoff) {
817         ry += ix * f->yoff;
818         iy = ry / f->src->xsize;
819       }
820       rx -= ix * f->src->xsize;
821       ry -= iy * f->src->ysize;
822       i_gpix(f->src, rx, ry, out);
823       ++out;
824       ++i;
825     }
826   }
827
828   if (fill->combine) {
829     (fill->combine)(data, work, channels, width);
830   }
831 }
832
833 /*
834 =item fill_image(fill, x, y, width, channels, data, work)
835
836 =cut
837 */
838 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
839                        i_fcolor *data, i_fcolor *work) {
840   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
841   i_fcolor *out = fill->combine ? work : data;
842   int i = 0;
843   i_fcolor c;
844   
845   if (f->has_matrix) {
846     /* the hard way */
847     while (i < width) {
848       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
849       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
850       double ix = floor(rx / f->src->xsize);
851       double iy = floor(ry / f->src->ysize);
852       i_fcolor c[2][2];
853       i_fcolor c2[2];
854       int dy;
855
856       if (f->xoff) {
857         rx += iy * f->xoff;
858         ix = floor(rx / f->src->xsize);
859       }
860       else if (f->yoff) {
861         ry += ix * f->yoff;
862         iy = floor(ry / f->src->ysize);
863       }
864       rx -= ix * f->src->xsize;
865       ry -= iy * f->src->ysize;
866
867       for (dy = 0; dy < 2; ++dy) {
868         if ((int)rx == f->src->xsize-1) {
869           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
870           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
871         }
872         else {
873           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
874                  c[dy]);
875         }
876         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
877       }
878       *out++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
879       ++i;
880     }
881   }
882   else {
883     /* the easy way */
884     /* this should be possible to optimize to use i_glin() */
885     while (i < width) {
886       int rx = x+i;
887       int ry = y;
888       int ix = rx / f->src->xsize;
889       int iy = ry / f->src->ysize;
890
891       if (f->xoff) {
892         rx += iy * f->xoff;
893         ix = rx / f->src->xsize;
894       }
895       else if (f->yoff) {
896         ry += ix * f->yoff;
897         iy = ry / f->src->xsize;
898       }
899       rx -= ix * f->src->xsize;
900       ry -= iy * f->src->ysize;
901       i_gpixf(f->src, rx, ry, out);
902       ++out;
903       ++i;
904     }
905   }
906
907   if (fill->combinef) {
908     (fill->combinef)(data, work, channels, width);
909   }
910 }
911
912 static void combine_replace(i_color *, i_color *, int, int);
913 static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
914 static void combine_alphablend(i_color *, i_color *, int, int);
915 static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
916 static void combine_mult(i_color *, i_color *, int, int);
917 static void combine_multf(i_fcolor *, i_fcolor *, int, int);
918 static void combine_dissolve(i_color *, i_color *, int, int);
919 static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
920 static void combine_add(i_color *, i_color *, int, int);
921 static void combine_addf(i_fcolor *, i_fcolor *, int, int);
922 static void combine_subtract(i_color *, i_color *, int, int);
923 static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
924 static void combine_diff(i_color *, i_color *, int, int);
925 static void combine_difff(i_fcolor *, i_fcolor *, int, int);
926 static void combine_darken(i_color *, i_color *, int, int);
927 static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
928 static void combine_lighten(i_color *, i_color *, int, int);
929 static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
930 static void combine_hue(i_color *, i_color *, int, int);
931 static void combine_huef(i_fcolor *, i_fcolor *, int, int);
932 static void combine_sat(i_color *, i_color *, int, int);
933 static void combine_satf(i_fcolor *, i_fcolor *, int, int);
934 static void combine_value(i_color *, i_color *, int, int);
935 static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
936 static void combine_color(i_color *, i_color *, int, int);
937 static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
938
939 struct i_combines {
940   i_fill_combine_f combine;
941   i_fill_combinef_f combinef;
942 } combines[] =
943 {
944   { /* replace */
945     combine_replace,
946     combine_replacef,
947   },
948   { /* alpha blend */
949     combine_alphablend,
950     combine_alphablendf,
951   },
952   {
953     /* multiply */
954     combine_mult,
955     combine_multf,
956   },
957   {
958     /* dissolve */
959     combine_dissolve,
960     combine_dissolvef,
961   },
962   {
963     /* add */
964     combine_add,
965     combine_addf,
966   },
967   {
968     /* subtract */
969     combine_subtract,
970     combine_subtractf,
971   },
972   {
973     /* diff */
974     combine_diff,
975     combine_difff,
976   },
977   {
978     combine_lighten,
979     combine_lightenf,
980   },
981   {
982     combine_darken,
983     combine_darkenf,
984   },
985   {
986     combine_hue,
987     combine_huef,
988   },
989   {
990     combine_sat,
991     combine_satf,
992   },
993   {
994     combine_value,
995     combine_valuef,
996   },
997   {
998     combine_color,
999     combine_colorf,
1000   },
1001 };
1002
1003 /*
1004 =item i_get_combine(combine, color_func, fcolor_func)
1005
1006 =cut
1007 */
1008
1009 void i_get_combine(int combine, i_fill_combine_f *color_func, 
1010                    i_fill_combinef_f *fcolor_func) {
1011   if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
1012     combine = 0;
1013
1014   *color_func = combines[combine].combine;
1015   *fcolor_func = combines[combine].combinef;
1016 }
1017
1018 static void combine_replace(i_color *out, i_color *in, int channels, int count) {
1019   while (count--) {
1020     *out++ = *in++;
1021   }
1022 }
1023
1024 static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1025   while (count--) {
1026     *out++ = *in++;
1027   }
1028 }
1029
1030 static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
1031   while (count--) {
1032     COMBINE(*out, *in, channels);
1033     ++out;
1034     ++in;
1035   }
1036 }
1037
1038 static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1039   while (count--) {
1040     COMBINEF(*out, *in, channels);
1041     ++out;
1042     ++in;
1043   }
1044 }
1045
1046 static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1047   int ch;
1048
1049   while (count--) {
1050     i_color c = *in;
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   int ch;
1080
1081   while (count--) {
1082     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1083       COMBINE(*out, *in, channels);
1084     ++out;
1085     ++in;
1086   }
1087 }
1088
1089 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1090   int ch;
1091
1092   while (count--) {
1093     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1094       COMBINEF(*out, *in, channels);
1095     ++out;
1096     ++in;
1097   }
1098 }
1099
1100 static void combine_add(i_color *out, i_color *in, int channels, int count) {
1101   int ch;
1102
1103   while (count--) {
1104     i_color c = *in;
1105     for (ch = 0; ch < (channels); ++ch) { 
1106       if (ch != 3) {
1107         int total = out->channel[ch] + in->channel[ch];
1108         if (total > 255)
1109           total = 255;
1110         c.channel[ch] = total;
1111       }
1112     } 
1113     COMBINE(*out, c, channels);
1114     ++out;
1115     ++in;
1116   }
1117 }
1118
1119 static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1120   int ch;
1121
1122   while (count--) {
1123     i_fcolor c = *in;
1124     for (ch = 0; ch < (channels); ++ch) { 
1125       if (ch != 3) {
1126         double total = out->channel[ch] + in->channel[ch];
1127         if (total > 1.0)
1128           total = 1.0;
1129         out->channel[ch] = total;
1130       }
1131     } 
1132     COMBINEF(*out, c, channels);
1133     ++out;
1134     ++in;
1135   }
1136 }
1137
1138 static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
1139   int ch;
1140
1141   while (count--) {
1142     i_color c = *in;
1143     for (ch = 0; ch < (channels); ++ch) { 
1144       if (ch != 3) {
1145         int total = out->channel[ch] - in->channel[ch];
1146         if (total < 0)
1147           total = 0;
1148         c.channel[ch] = total;
1149       }
1150     } 
1151     COMBINE(*out, c, channels);
1152     ++out;
1153     ++in;
1154   }
1155 }
1156
1157 static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1158   int ch;
1159
1160   while (count--) {
1161     i_fcolor c = *in;
1162     for (ch = 0; ch < channels; ++ch) { 
1163       if (ch != 3) {
1164         double total = out->channel[ch] - in->channel[ch];
1165         if (total < 0)
1166           total = 0;
1167         c.channel[ch] = total;
1168       }
1169     } 
1170     COMBINEF(*out, c, channels);
1171     ++out;
1172     ++in;
1173   }
1174 }
1175
1176 static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1177   int ch;
1178
1179   while (count--) {
1180     i_color c = *in;
1181     for (ch = 0; ch < (channels); ++ch) { 
1182       if (ch != 3) 
1183         c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1184     } 
1185     COMBINE(*out, c, channels)
1186     ++out;
1187     ++in;
1188   }
1189 }
1190
1191 static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
1192   int ch;
1193
1194   while (count--) {
1195     i_fcolor c = *in;
1196     for (ch = 0; ch < (channels); ++ch) { 
1197       if (ch != 3)
1198         c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
1199     }
1200     COMBINEF(*out, c, channels);
1201     ++out;
1202     ++in;
1203   }
1204 }
1205
1206 static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1207   int ch;
1208
1209   while (count--) {
1210     for (ch = 0; ch < channels; ++ch) { 
1211       if (ch != 3 && out->channel[ch] < in->channel[ch])
1212         in->channel[ch] = out->channel[ch];
1213     } 
1214     COMBINE(*out, *in, channels);
1215     ++out;
1216     ++in;
1217   }
1218 }
1219
1220 static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1221   int ch;
1222
1223   while (count--) {
1224     for (ch = 0; ch < channels; ++ch) { 
1225       if (ch != 3 && out->channel[ch] < in->channel[ch])
1226         in->channel[ch] = out->channel[ch];
1227     } 
1228     COMBINEF(*out, *in, channels);
1229     ++out;
1230     ++in;
1231   }
1232 }
1233
1234 static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1235   int ch;
1236
1237   while (count--) {
1238     for (ch = 0; ch < channels; ++ch) { 
1239       if (ch != 3 && out->channel[ch] > in->channel[ch])
1240         in->channel[ch] = out->channel[ch];
1241     } 
1242     COMBINE(*out, *in, channels);
1243     ++out;
1244     ++in;
1245   }
1246 }
1247
1248 static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1249   int ch;
1250
1251   while (count--) {
1252     for (ch = 0; ch < channels; ++ch) { 
1253       if (ch != 3 && out->channel[ch] > in->channel[ch])
1254         in->channel[ch] = out->channel[ch];
1255     } 
1256     COMBINEF(*out, *in, channels);
1257     ++out;
1258     ++in;
1259   }
1260 }
1261
1262 static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1263   while (count--) {
1264     i_color c = *out;
1265     i_rgb_to_hsv(&c);
1266     i_rgb_to_hsv(in);
1267     c.channel[0] = in->channel[0];
1268     i_hsv_to_rgb(&c);
1269     c.channel[3] = in->channel[3];
1270     COMBINE(*out, c, channels);
1271     ++out;
1272     ++in;
1273   }
1274 }
1275
1276 static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1277   while (count--) {
1278     i_fcolor c = *out;
1279     i_rgb_to_hsvf(&c);
1280     i_rgb_to_hsvf(in);
1281     c.channel[0] = in->channel[0];
1282     i_hsv_to_rgbf(&c);
1283     c.channel[3] = in->channel[3];
1284     COMBINEF(*out, c, channels);
1285     ++out;
1286     ++in;
1287   }
1288 }
1289
1290 static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1291   while (count--) {
1292     i_color c = *out;
1293     i_rgb_to_hsv(&c);
1294     i_rgb_to_hsv(in);
1295     c.channel[1] = in->channel[1];
1296     i_hsv_to_rgb(&c);
1297     c.channel[3] = in->channel[3];
1298     COMBINE(*out, c, channels);
1299     ++out;
1300     ++in;
1301   }
1302 }
1303
1304 static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1305   while (count--) {
1306     i_fcolor c = *out;
1307     i_rgb_to_hsvf(&c);
1308     i_rgb_to_hsvf(in);
1309     c.channel[1] = in->channel[1];
1310     i_hsv_to_rgbf(&c);
1311     c.channel[3] = in->channel[3];
1312     COMBINEF(*out, c, channels);
1313     ++out;
1314     ++in;
1315   }
1316 }
1317
1318 static void combine_value(i_color *out, i_color *in, int channels, int count) {
1319   while (count--) {
1320     i_color c = *out;
1321     i_rgb_to_hsv(&c);
1322     i_rgb_to_hsv(in);
1323     c.channel[2] = in->channel[2];
1324     i_hsv_to_rgb(&c);
1325     c.channel[3] = in->channel[3];
1326     COMBINE(*out, c, channels);
1327     ++out;
1328     ++in;
1329   }
1330 }
1331
1332 static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
1333                            int count) {
1334   while (count--) {
1335     i_fcolor c = *out;
1336     i_rgb_to_hsvf(&c);
1337     i_rgb_to_hsvf(in);
1338     c.channel[2] = in->channel[2];
1339     i_hsv_to_rgbf(&c);
1340     c.channel[3] = in->channel[3];
1341     COMBINEF(*out, c, channels);
1342     ++out;
1343     ++in;
1344   }
1345 }
1346
1347 static void combine_color(i_color *out, i_color *in, int channels, int count) {
1348   while (count--) {
1349     i_color c = *out;
1350     i_rgb_to_hsv(&c);
1351     i_rgb_to_hsv(in);
1352     c.channel[0] = in->channel[0];
1353     c.channel[1] = in->channel[1];
1354     i_hsv_to_rgb(&c);
1355     c.channel[3] = in->channel[3];
1356     COMBINE(*out, c, channels);
1357     ++out;
1358     ++in;
1359   }
1360 }
1361
1362 static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
1363                            int count) {
1364   while (count--) {
1365     i_fcolor c = *out;
1366     i_rgb_to_hsvf(&c);
1367     i_rgb_to_hsvf(in);
1368     c.channel[0] = in->channel[0];
1369     c.channel[1] = in->channel[1];
1370     i_hsv_to_rgbf(&c);
1371     c.channel[3] = in->channel[3];
1372     COMBINEF(*out, c, channels);
1373     ++out;
1374     ++in;
1375   }
1376 }
1377
1378
1379 /*
1380 =back
1381
1382 =head1 AUTHOR
1383
1384 Tony Cook <tony@develop-help.com>
1385
1386 =head1 SEE ALSO
1387
1388 Imager(3)
1389
1390 =cut
1391 */