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