]> git.imager.perl.org - imager.git/blob - fills.c
fill out the large sample support docs
[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     if (byte & mask)
680       *data++ = f->fg;
681     else
682       *data++ = f->bg;
683     
684     if ((mask >>= 1) == 0)
685       mask = 128;
686   }
687 }
688
689 /*
690 =item fill_hatchf(fill, x, y, width, channels, data)
691
692 The floating sample fill function for hatched fills.
693
694 =back
695 */
696 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
697                         i_fcolor *data) {
698   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
699   int byte = f->hatch[(y + f->dy) & 7];
700   int xpos = (x + f->dx) & 7;
701   int mask = 128 >> xpos;
702   
703   while (width-- > 0) {
704     if (byte & mask)
705       *data++ = f->ffg;
706     else
707       *data++ = f->fbg;
708     
709     if ((mask >>= 1) == 0)
710       mask = 128;
711   }
712 }
713
714 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
715 /* linear interpolation */
716 static i_color interp_i_color(i_color before, i_color after, double pos,
717                               int channels) {
718   i_color out;
719   int ch;
720
721   pos -= floor(pos);
722   for (ch = 0; ch < channels; ++ch)
723     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
724   if (channels > 3 && out.channel[3])
725     for (ch = 0; ch < channels; ++ch)
726       if (ch != 3) {
727         int temp = out.channel[ch] * 255 / out.channel[3];
728         if (temp > 255)
729           temp = 255;
730         out.channel[ch] = temp;
731       }
732
733   return out;
734 }
735
736 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
737 /* linear interpolation */
738 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
739                                 int channels) {
740   i_fcolor out;
741   int ch;
742
743   pos -= floor(pos);
744   for (ch = 0; ch < channels; ++ch)
745     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
746   if (out.channel[3])
747     for (ch = 0; ch < channels; ++ch)
748       if (ch != 3) {
749         int temp = out.channel[ch] / out.channel[3];
750         if (temp > 1.0)
751           temp = 1.0;
752         out.channel[ch] = temp;
753       }
754
755   return out;
756 }
757
758 /*
759 =item fill_image(fill, x, y, width, channels, data, work)
760
761 =cut
762 */
763 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
764                        i_color *data) {
765   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
766   int i = 0;
767   i_color *out = data;
768   
769   if (f->has_matrix) {
770     /* the hard way */
771     while (i < width) {
772       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
773       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
774       double ix = floor(rx / f->src->xsize);
775       double iy = floor(ry / f->src->ysize);
776       i_color c[2][2];
777       i_color c2[2];
778       int dy;
779
780       if (f->xoff) {
781         rx += iy * f->xoff;
782         ix = floor(rx / f->src->xsize);
783       }
784       else if (f->yoff) {
785         ry += ix * f->yoff;
786         iy = floor(ry / f->src->ysize);
787       }
788       rx -= ix * f->src->xsize;
789       ry -= iy * f->src->ysize;
790
791       for (dy = 0; dy < 2; ++dy) {
792         if ((int)rx == f->src->xsize-1) {
793           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
794           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
795         }
796         else {
797           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
798                  c[dy]);
799         }
800         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
801       }
802       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
803       ++i;
804     }
805   }
806   else {
807     /* the easy way */
808     /* this should be possible to optimize to use i_glin() */
809     while (i < width) {
810       int rx = x+i;
811       int ry = y;
812       int ix = rx / f->src->xsize;
813       int iy = ry / f->src->ysize;
814
815       if (f->xoff) {
816         rx += iy * f->xoff;
817         ix = rx / f->src->xsize;
818       }
819       else if (f->yoff) {
820         ry += ix * f->yoff;
821         iy = ry / f->src->xsize;
822       }
823       rx -= ix * f->src->xsize;
824       ry -= iy * f->src->ysize;
825       i_gpix(f->src, rx, ry, out);
826       ++out;
827       ++i;
828     }
829   }
830   if (f->src->channels == 3) {
831     /* just set the alpha */
832     for (i = 0; i <  width; ++i) {
833       data->channel[3] = 255;
834       data++;
835     }
836   }
837   else if (f->src->channels == 2) {
838     /* copy the alpha to channel 3, duplicate the grey value */
839     for (i = 0; i <  width; ++i) {
840       data->channel[3] = data->channel[1];
841       data->channel[1] = data->channel[2] = data->channel[0];
842       data++;
843     }
844   }
845   else if (f->src->channels == 1) {
846     /* set the alpha, duplicate grey */
847     for (i = 0; i <  width; ++i) {
848       data->channel[3] = 255;
849       data->channel[1] = data->channel[2] = data->channel[0];
850       data++;
851     }
852   }
853 }
854
855 /*
856 =item fill_image(fill, x, y, width, channels, data, work)
857
858 =cut
859 */
860 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
861                        i_fcolor *data) {
862   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
863   int i = 0;
864   
865   if (f->has_matrix) {
866     /* the hard way */
867     while (i < width) {
868       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
869       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
870       double ix = floor(rx / f->src->xsize);
871       double iy = floor(ry / f->src->ysize);
872       i_fcolor c[2][2];
873       i_fcolor c2[2];
874       int dy;
875
876       if (f->xoff) {
877         rx += iy * f->xoff;
878         ix = floor(rx / f->src->xsize);
879       }
880       else if (f->yoff) {
881         ry += ix * f->yoff;
882         iy = floor(ry / f->src->ysize);
883       }
884       rx -= ix * f->src->xsize;
885       ry -= iy * f->src->ysize;
886
887       for (dy = 0; dy < 2; ++dy) {
888         if ((int)rx == f->src->xsize-1) {
889           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
890           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
891         }
892         else {
893           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
894                  c[dy]);
895         }
896         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
897       }
898       *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
899       ++i;
900     }
901   }
902   else {
903     /* the easy way */
904     /* this should be possible to optimize to use i_glin() */
905     while (i < width) {
906       int rx = x+i;
907       int ry = y;
908       int ix = rx / f->src->xsize;
909       int iy = ry / f->src->ysize;
910
911       if (f->xoff) {
912         rx += iy * f->xoff;
913         ix = rx / f->src->xsize;
914       }
915       else if (f->yoff) {
916         ry += ix * f->yoff;
917         iy = ry / f->src->xsize;
918       }
919       rx -= ix * f->src->xsize;
920       ry -= iy * f->src->ysize;
921       i_gpixf(f->src, rx, ry, data);
922       ++data;
923       ++i;
924     }
925   }
926   if (f->src->channels == 3) {
927     /* just set the alpha */
928     for (i = 0; i <  width; ++i) {
929       data->channel[3] = 1.0;
930       data++;
931     }
932   }
933   else if (f->src->channels == 2) {
934     /* copy the alpha to channel 3, duplicate the grey value */
935     for (i = 0; i <  width; ++i) {
936       data->channel[3] = data->channel[1];
937       data->channel[1] = data->channel[2] = data->channel[0];
938       data++;
939     }
940   }
941   else if (f->src->channels == 1) {
942     /* set the alpha, duplicate grey */
943     for (i = 0; i <  width; ++i) {
944       data->channel[3] = 1.0;
945       data->channel[1] = data->channel[2] = data->channel[0];
946       data++;
947     }
948   }
949 }
950
951 static void combine_replace(i_color *, i_color *, int, int);
952 static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
953 static void combine_alphablend(i_color *, i_color *, int, int);
954 static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
955 static void combine_mult(i_color *, i_color *, int, int);
956 static void combine_multf(i_fcolor *, i_fcolor *, int, int);
957 static void combine_dissolve(i_color *, i_color *, int, int);
958 static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
959 static void combine_add(i_color *, i_color *, int, int);
960 static void combine_addf(i_fcolor *, i_fcolor *, int, int);
961 static void combine_subtract(i_color *, i_color *, int, int);
962 static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
963 static void combine_diff(i_color *, i_color *, int, int);
964 static void combine_difff(i_fcolor *, i_fcolor *, int, int);
965 static void combine_darken(i_color *, i_color *, int, int);
966 static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
967 static void combine_lighten(i_color *, i_color *, int, int);
968 static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
969 static void combine_hue(i_color *, i_color *, int, int);
970 static void combine_huef(i_fcolor *, i_fcolor *, int, int);
971 static void combine_sat(i_color *, i_color *, int, int);
972 static void combine_satf(i_fcolor *, i_fcolor *, int, int);
973 static void combine_value(i_color *, i_color *, int, int);
974 static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
975 static void combine_color(i_color *, i_color *, int, int);
976 static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
977
978 static struct i_combines {
979   i_fill_combine_f combine;
980   i_fill_combinef_f combinef;
981 } combines[] =
982 {
983   { /* replace */
984     combine_replace,
985     combine_replacef,
986   },
987   { /* alpha blend */
988     combine_alphablend,
989     combine_alphablendf,
990   },
991   {
992     /* multiply */
993     combine_mult,
994     combine_multf,
995   },
996   {
997     /* dissolve */
998     combine_dissolve,
999     combine_dissolvef,
1000   },
1001   {
1002     /* add */
1003     combine_add,
1004     combine_addf,
1005   },
1006   {
1007     /* subtract */
1008     combine_subtract,
1009     combine_subtractf,
1010   },
1011   {
1012     /* diff */
1013     combine_diff,
1014     combine_difff,
1015   },
1016   {
1017     combine_lighten,
1018     combine_lightenf,
1019   },
1020   {
1021     combine_darken,
1022     combine_darkenf,
1023   },
1024   {
1025     combine_hue,
1026     combine_huef,
1027   },
1028   {
1029     combine_sat,
1030     combine_satf,
1031   },
1032   {
1033     combine_value,
1034     combine_valuef,
1035   },
1036   {
1037     combine_color,
1038     combine_colorf,
1039   },
1040 };
1041
1042 /*
1043 =item i_get_combine(combine, color_func, fcolor_func)
1044
1045 =cut
1046 */
1047
1048 void i_get_combine(int combine, i_fill_combine_f *color_func, 
1049                    i_fill_combinef_f *fcolor_func) {
1050   if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
1051     combine = 0;
1052
1053   *color_func = combines[combine].combine;
1054   *fcolor_func = combines[combine].combinef;
1055 }
1056
1057 static void combine_replace(i_color *out, i_color *in, int channels, int count) {
1058   while (count--) {
1059     *out++ = *in++;
1060   }
1061 }
1062
1063 static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1064   while (count--) {
1065     *out++ = *in++;
1066   }
1067 }
1068
1069 static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
1070   while (count--) {
1071     COMBINE(*out, *in, channels);
1072     ++out;
1073     ++in;
1074   }
1075 }
1076
1077 static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1078   while (count--) {
1079     COMBINEF(*out, *in, channels);
1080     ++out;
1081     ++in;
1082   }
1083 }
1084
1085 static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1086   int ch;
1087
1088   while (count--) {
1089     double mult[MAXCHANNELS];
1090     mult[3] = in->channel[3];
1091     for (ch = 0; ch < (channels); ++ch) { 
1092       if (ch != 3)
1093         mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
1094     } 
1095     COMBINEA(*out, mult, channels);
1096     ++out;
1097     ++in;
1098   }
1099 }
1100
1101 static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1102   int ch;
1103
1104   while (count--) {
1105     i_fcolor c = *in;
1106     for (ch = 0; ch < channels; ++ch) { 
1107       if (ch != 3)
1108         c.channel[ch] = out->channel[ch] * in->channel[ch];
1109     } 
1110     COMBINEF(*out, c, channels);
1111     ++out;
1112     ++in;
1113   }
1114 }
1115
1116 static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
1117   while (count--) {
1118     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1119       COMBINE(*out, *in, channels);
1120     ++out;
1121     ++in;
1122   }
1123 }
1124
1125 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1126   while (count--) {
1127     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1128       COMBINEF(*out, *in, channels);
1129     ++out;
1130     ++in;
1131   }
1132 }
1133
1134 static void combine_add(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 > 255)
1143           total = 255;
1144         c.channel[ch] = total;
1145       }
1146     } 
1147     COMBINE(*out, c, channels);
1148     ++out;
1149     ++in;
1150   }
1151 }
1152
1153 static void combine_addf(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 > 1.0)
1162           total = 1.0;
1163         out->channel[ch] = total;
1164       }
1165     } 
1166     COMBINEF(*out, c, channels);
1167     ++out;
1168     ++in;
1169   }
1170 }
1171
1172 static void combine_subtract(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         int total = out->channel[ch] - in->channel[ch];
1180         if (total < 0)
1181           total = 0;
1182         c.channel[ch] = total;
1183       }
1184     } 
1185     COMBINE(*out, c, channels);
1186     ++out;
1187     ++in;
1188   }
1189 }
1190
1191 static void combine_subtractf(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         double total = out->channel[ch] - in->channel[ch];
1199         if (total < 0)
1200           total = 0;
1201         c.channel[ch] = total;
1202       }
1203     } 
1204     COMBINEF(*out, c, channels);
1205     ++out;
1206     ++in;
1207   }
1208 }
1209
1210 static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1211   int ch;
1212
1213   while (count--) {
1214     i_color c = *in;
1215     for (ch = 0; ch < (channels); ++ch) { 
1216       if (ch != 3) 
1217         c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1218     } 
1219     COMBINE(*out, c, channels)
1220     ++out;
1221     ++in;
1222   }
1223 }
1224
1225 static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
1226   int ch;
1227
1228   while (count--) {
1229     i_fcolor c = *in;
1230     for (ch = 0; ch < (channels); ++ch) { 
1231       if (ch != 3)
1232         c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
1233     }
1234     COMBINEF(*out, c, channels);
1235     ++out;
1236     ++in;
1237   }
1238 }
1239
1240 static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1241   int ch;
1242
1243   while (count--) {
1244     for (ch = 0; ch < channels; ++ch) { 
1245       if (ch != 3 && out->channel[ch] < in->channel[ch])
1246         in->channel[ch] = out->channel[ch];
1247     } 
1248     COMBINE(*out, *in, channels);
1249     ++out;
1250     ++in;
1251   }
1252 }
1253
1254 static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1255   int ch;
1256
1257   while (count--) {
1258     for (ch = 0; ch < channels; ++ch) { 
1259       if (ch != 3 && out->channel[ch] < in->channel[ch])
1260         in->channel[ch] = out->channel[ch];
1261     } 
1262     COMBINEF(*out, *in, channels);
1263     ++out;
1264     ++in;
1265   }
1266 }
1267
1268 static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1269   int ch;
1270
1271   while (count--) {
1272     for (ch = 0; ch < channels; ++ch) { 
1273       if (ch != 3 && out->channel[ch] > in->channel[ch])
1274         in->channel[ch] = out->channel[ch];
1275     } 
1276     COMBINE(*out, *in, channels);
1277     ++out;
1278     ++in;
1279   }
1280 }
1281
1282 static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1283   int ch;
1284
1285   while (count--) {
1286     for (ch = 0; ch < channels; ++ch) { 
1287       if (ch != 3 && out->channel[ch] > in->channel[ch])
1288         in->channel[ch] = out->channel[ch];
1289     } 
1290     COMBINEF(*out, *in, channels);
1291     ++out;
1292     ++in;
1293   }
1294 }
1295
1296 static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1297   while (count--) {
1298     i_color c = *out;
1299     i_rgb_to_hsv(&c);
1300     i_rgb_to_hsv(in);
1301     c.channel[0] = in->channel[0];
1302     i_hsv_to_rgb(&c);
1303     c.channel[3] = in->channel[3];
1304     COMBINE(*out, c, channels);
1305     ++out;
1306     ++in;
1307   }
1308 }
1309
1310 static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1311   while (count--) {
1312     i_fcolor c = *out;
1313     i_rgb_to_hsvf(&c);
1314     i_rgb_to_hsvf(in);
1315     c.channel[0] = in->channel[0];
1316     i_hsv_to_rgbf(&c);
1317     c.channel[3] = in->channel[3];
1318     COMBINEF(*out, c, channels);
1319     ++out;
1320     ++in;
1321   }
1322 }
1323
1324 static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1325   while (count--) {
1326     i_color c = *out;
1327     i_rgb_to_hsv(&c);
1328     i_rgb_to_hsv(in);
1329     c.channel[1] = in->channel[1];
1330     i_hsv_to_rgb(&c);
1331     c.channel[3] = in->channel[3];
1332     COMBINE(*out, c, channels);
1333     ++out;
1334     ++in;
1335   }
1336 }
1337
1338 static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1339   while (count--) {
1340     i_fcolor c = *out;
1341     i_rgb_to_hsvf(&c);
1342     i_rgb_to_hsvf(in);
1343     c.channel[1] = in->channel[1];
1344     i_hsv_to_rgbf(&c);
1345     c.channel[3] = in->channel[3];
1346     COMBINEF(*out, c, channels);
1347     ++out;
1348     ++in;
1349   }
1350 }
1351
1352 static void combine_value(i_color *out, i_color *in, int channels, int count) {
1353   while (count--) {
1354     i_color c = *out;
1355     i_rgb_to_hsv(&c);
1356     i_rgb_to_hsv(in);
1357     c.channel[2] = in->channel[2];
1358     i_hsv_to_rgb(&c);
1359     c.channel[3] = in->channel[3];
1360     COMBINE(*out, c, channels);
1361     ++out;
1362     ++in;
1363   }
1364 }
1365
1366 static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
1367                            int count) {
1368   while (count--) {
1369     i_fcolor c = *out;
1370     i_rgb_to_hsvf(&c);
1371     i_rgb_to_hsvf(in);
1372     c.channel[2] = in->channel[2];
1373     i_hsv_to_rgbf(&c);
1374     c.channel[3] = in->channel[3];
1375     COMBINEF(*out, c, channels);
1376     ++out;
1377     ++in;
1378   }
1379 }
1380
1381 static void combine_color(i_color *out, i_color *in, int channels, int count) {
1382   while (count--) {
1383     i_color c = *out;
1384     i_rgb_to_hsv(&c);
1385     i_rgb_to_hsv(in);
1386     c.channel[0] = in->channel[0];
1387     c.channel[1] = in->channel[1];
1388     i_hsv_to_rgb(&c);
1389     c.channel[3] = in->channel[3];
1390     COMBINE(*out, c, channels);
1391     ++out;
1392     ++in;
1393   }
1394 }
1395
1396 static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
1397                            int count) {
1398   while (count--) {
1399     i_fcolor c = *out;
1400     i_rgb_to_hsvf(&c);
1401     i_rgb_to_hsvf(in);
1402     c.channel[0] = in->channel[0];
1403     c.channel[1] = in->channel[1];
1404     i_hsv_to_rgbf(&c);
1405     c.channel[3] = in->channel[3];
1406     COMBINEF(*out, c, channels);
1407     ++out;
1408     ++in;
1409   }
1410 }
1411
1412
1413 /*
1414 =back
1415
1416 =head1 AUTHOR
1417
1418 Tony Cook <tony@develop-help.com>
1419
1420 =head1 SEE ALSO
1421
1422 Imager(3)
1423
1424 =cut
1425 */