- start of external Imager API access:
[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(i_fcolor *c) {
108   int ch;
109   i_color out;
110
111   for (ch = 0; ch < MAXCHANNELS; ++ch)
112     out.channel[ch] = SampleFTo8(c->channel[ch]);
113
114   return out;
115 }
116
117 static i_fcolor color_to_fcolor(i_color *c) {
118   int ch;
119   i_fcolor out;
120
121   for (ch = 0; ch < MAXCHANNELS; ++ch)
122     out.channel[ch] = Sample8ToF(c->channel[ch]);
123
124   return out;
125 }
126
127 /* alpha combine in with out */
128 #define COMBINE(out, in, channels) \
129   { \
130     int ch; \
131     for (ch = 0; ch < (channels); ++ch) { \
132       (out).channel[ch] = ((out).channel[ch] * (255 - (in).channel[3]) \
133         + (in).channel[ch] * (in).channel[3]) / 255; \
134     } \
135   }
136
137 /* alpha combine in with out, in this case in is a simple array of
138    samples, potentially not integers - the mult combiner uses doubles
139    for accuracy */
140 #define COMBINEA(out, in, channels) \
141   { \
142     int ch; \
143     for (ch = 0; ch < (channels); ++ch) { \
144       (out).channel[ch] = ((out).channel[ch] * (255 - (in)[3]) \
145         + (in)[ch] * (in)[3]) / 255; \
146     } \
147   }
148
149 #define COMBINEF(out, in, channels) \
150   { \
151     int ch; \
152     for (ch = 0; ch < (channels); ++ch) { \
153       (out).channel[ch] = (out).channel[ch] * (1.0 - (in).channel[3]) \
154         + (in).channel[ch] * (in).channel[3]; \
155     } \
156   }
157
158 typedef struct
159 {
160   i_fill_t base;
161   i_color c;
162   i_fcolor fc;
163 } i_fill_solid_t;
164
165 static void fill_solid(i_fill_t *, int x, int y, int width, int channels, 
166                        i_color *);
167 static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, 
168                         i_fcolor *);
169 static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels, 
170                             i_color *);
171 static void fill_solidf_comb(i_fill_t *, int x, int y, int width, 
172                              int channels, i_fcolor *);
173
174 static i_fill_solid_t base_solid_fill =
175 {
176   {
177     fill_solid,
178     fill_solidf,
179     NULL,
180     NULL,
181     NULL,
182   },
183 };
184 static i_fill_solid_t base_solid_fill_comb =
185 {
186   {
187     fill_solid_comb,
188     fill_solidf_comb,
189     NULL,
190     NULL,
191     NULL,
192   },
193 };
194
195 /*
196 =item i_fill_destroy(fill)
197
198 =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(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(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(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, 
423                 int combine, int hatch, 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(i_color *fg, i_color *bg, int combine, int hatch, 
446             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(i_fcolor *fg, i_fcolor *bg, int combine, int hatch, 
471             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, 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(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg, 
616                 int combine, int hatch, unsigned char *cust_hatch,
617                 int dx, int dy) {
618   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
619
620   fill->base.fill_with_color = fill_hatch;
621   fill->base.fill_with_fcolor = fill_hatchf;
622   fill->base.destroy = NULL;
623   fill->fg = fg ? *fg : fcolor_to_color(ffg);
624   fill->bg = bg ? *bg : fcolor_to_color(fbg);
625   fill->ffg = ffg ? *ffg : color_to_fcolor(fg);
626   fill->fbg = fbg ? *fbg : color_to_fcolor(bg);
627   if (combine) {
628     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
629   }
630   else {
631     fill->base.combine = NULL;
632     fill->base.combinef = NULL;
633   }
634   if (cust_hatch) {
635     memcpy(fill->hatch, cust_hatch, 8);
636   }
637   else {
638     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
639       hatch = 0;
640     memcpy(fill->hatch, builtin_hatches[hatch], 8);
641   }
642   fill->dx = dx & 7;
643   fill->dy = dy & 7;
644
645   return &fill->base;
646 }
647
648 /*
649 =item fill_hatch(fill, x, y, width, channels, data)
650
651 The 8-bit sample fill function for hatched fills.
652
653 =cut
654 */
655 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
656                        i_color *data) {
657   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
658   int byte = f->hatch[(y + f->dy) & 7];
659   int xpos = (x + f->dx) & 7;
660   int mask = 128 >> xpos;
661
662   while (width-- > 0) {
663     *data++ = (byte & mask) ? f->fg : f->bg;
664     
665     if ((mask >>= 1) == 0)
666       mask = 128;
667   }
668 }
669
670 /*
671 =item fill_hatchf(fill, x, y, width, channels, data)
672
673 The floating sample fill function for hatched fills.
674
675 =back
676 */
677 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
678                         i_fcolor *data) {
679   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
680   int byte = f->hatch[(y + f->dy) & 7];
681   int xpos = (x + f->dx) & 7;
682   int mask = 128 >> xpos;
683   
684   while (width-- > 0) {
685     *data++ = (byte & mask) ? f->ffg : f->fbg;
686     
687     if ((mask >>= 1) == 0)
688       mask = 128;
689   }
690 }
691
692 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
693 /* linear interpolation */
694 static i_color interp_i_color(i_color before, i_color after, double pos,
695                               int channels) {
696   i_color out;
697   int ch;
698
699   pos -= floor(pos);
700   for (ch = 0; ch < channels; ++ch)
701     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
702   if (out.channel[3])
703     for (ch = 0; ch < channels; ++ch)
704       if (ch != 3) {
705         int temp = out.channel[ch] * 255 / out.channel[3];
706         if (temp > 255)
707           temp = 255;
708         out.channel[ch] = temp;
709       }
710
711   return out;
712 }
713
714 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
715 /* linear interpolation */
716 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
717                                 int channels) {
718   i_fcolor 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 (out.channel[3])
725     for (ch = 0; ch < channels; ++ch)
726       if (ch != 3) {
727         int temp = out.channel[ch] / out.channel[3];
728         if (temp > 1.0)
729           temp = 1.0;
730         out.channel[ch] = temp;
731       }
732
733   return out;
734 }
735
736 /*
737 =item fill_image(fill, x, y, width, channels, data, work)
738
739 =cut
740 */
741 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
742                        i_color *data) {
743   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
744   int i = 0;
745   i_color *out = data;
746   
747   if (f->has_matrix) {
748     /* the hard way */
749     while (i < width) {
750       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
751       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
752       double ix = floor(rx / f->src->xsize);
753       double iy = floor(ry / f->src->ysize);
754       i_color c[2][2];
755       i_color c2[2];
756       int dy;
757
758       if (f->xoff) {
759         rx += iy * f->xoff;
760         ix = floor(rx / f->src->xsize);
761       }
762       else if (f->yoff) {
763         ry += ix * f->yoff;
764         iy = floor(ry / f->src->ysize);
765       }
766       rx -= ix * f->src->xsize;
767       ry -= iy * f->src->ysize;
768
769       for (dy = 0; dy < 2; ++dy) {
770         if ((int)rx == f->src->xsize-1) {
771           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
772           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
773         }
774         else {
775           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
776                  c[dy]);
777         }
778         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
779       }
780       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
781       ++i;
782     }
783   }
784   else {
785     /* the easy way */
786     /* this should be possible to optimize to use i_glin() */
787     while (i < width) {
788       int rx = x+i;
789       int ry = y;
790       int ix = rx / f->src->xsize;
791       int iy = ry / f->src->ysize;
792
793       if (f->xoff) {
794         rx += iy * f->xoff;
795         ix = rx / f->src->xsize;
796       }
797       else if (f->yoff) {
798         ry += ix * f->yoff;
799         iy = ry / f->src->xsize;
800       }
801       rx -= ix * f->src->xsize;
802       ry -= iy * f->src->ysize;
803       i_gpix(f->src, rx, ry, out);
804       ++out;
805       ++i;
806     }
807   }
808   if (f->src->channels == 3) {
809     /* just set the alpha */
810     for (i = 0; i <  width; ++i) {
811       data->channel[3] = 255;
812       data++;
813     }
814   }
815   else if (f->src->channels == 2) {
816     /* copy the alpha to channel 3, duplicate the grey value */
817     for (i = 0; i <  width; ++i) {
818       data->channel[3] = data->channel[1];
819       data->channel[1] = data->channel[2] = data->channel[0];
820       data++;
821     }
822   }
823   else if (f->src->channels == 1) {
824     /* set the alpha, duplicate grey */
825     for (i = 0; i <  width; ++i) {
826       data->channel[3] = 255;
827       data->channel[1] = data->channel[2] = data->channel[0];
828       data++;
829     }
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) {
840   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
841   int i = 0;
842   
843   if (f->has_matrix) {
844     /* the hard way */
845     while (i < width) {
846       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
847       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
848       double ix = floor(rx / f->src->xsize);
849       double iy = floor(ry / f->src->ysize);
850       i_fcolor c[2][2];
851       i_fcolor c2[2];
852       int dy;
853
854       if (f->xoff) {
855         rx += iy * f->xoff;
856         ix = floor(rx / f->src->xsize);
857       }
858       else if (f->yoff) {
859         ry += ix * f->yoff;
860         iy = floor(ry / f->src->ysize);
861       }
862       rx -= ix * f->src->xsize;
863       ry -= iy * f->src->ysize;
864
865       for (dy = 0; dy < 2; ++dy) {
866         if ((int)rx == f->src->xsize-1) {
867           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
868           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
869         }
870         else {
871           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
872                  c[dy]);
873         }
874         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
875       }
876       *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
877       ++i;
878     }
879   }
880   else {
881     /* the easy way */
882     /* this should be possible to optimize to use i_glin() */
883     while (i < width) {
884       int rx = x+i;
885       int ry = y;
886       int ix = rx / f->src->xsize;
887       int iy = ry / f->src->ysize;
888
889       if (f->xoff) {
890         rx += iy * f->xoff;
891         ix = rx / f->src->xsize;
892       }
893       else if (f->yoff) {
894         ry += ix * f->yoff;
895         iy = ry / f->src->xsize;
896       }
897       rx -= ix * f->src->xsize;
898       ry -= iy * f->src->ysize;
899       i_gpixf(f->src, rx, ry, data);
900       ++data;
901       ++i;
902     }
903   }
904   if (f->src->channels == 3) {
905     /* just set the alpha */
906     for (i = 0; i <  width; ++i) {
907       data->channel[3] = 1.0;
908       data++;
909     }
910   }
911   else if (f->src->channels == 2) {
912     /* copy the alpha to channel 3, duplicate the grey value */
913     for (i = 0; i <  width; ++i) {
914       data->channel[3] = data->channel[1];
915       data->channel[1] = data->channel[2] = data->channel[0];
916       data++;
917     }
918   }
919   else if (f->src->channels == 1) {
920     /* set the alpha, duplicate grey */
921     for (i = 0; i <  width; ++i) {
922       data->channel[3] = 1.0;
923       data->channel[1] = data->channel[2] = data->channel[0];
924       data++;
925     }
926   }
927 }
928
929 static void combine_replace(i_color *, i_color *, int, int);
930 static void combine_replacef(i_fcolor *, i_fcolor *, int, int);
931 static void combine_alphablend(i_color *, i_color *, int, int);
932 static void combine_alphablendf(i_fcolor *, i_fcolor *, int, int);
933 static void combine_mult(i_color *, i_color *, int, int);
934 static void combine_multf(i_fcolor *, i_fcolor *, int, int);
935 static void combine_dissolve(i_color *, i_color *, int, int);
936 static void combine_dissolvef(i_fcolor *, i_fcolor *, int, int);
937 static void combine_add(i_color *, i_color *, int, int);
938 static void combine_addf(i_fcolor *, i_fcolor *, int, int);
939 static void combine_subtract(i_color *, i_color *, int, int);
940 static void combine_subtractf(i_fcolor *, i_fcolor *, int, int);
941 static void combine_diff(i_color *, i_color *, int, int);
942 static void combine_difff(i_fcolor *, i_fcolor *, int, int);
943 static void combine_darken(i_color *, i_color *, int, int);
944 static void combine_darkenf(i_fcolor *, i_fcolor *, int, int);
945 static void combine_lighten(i_color *, i_color *, int, int);
946 static void combine_lightenf(i_fcolor *, i_fcolor *, int, int);
947 static void combine_hue(i_color *, i_color *, int, int);
948 static void combine_huef(i_fcolor *, i_fcolor *, int, int);
949 static void combine_sat(i_color *, i_color *, int, int);
950 static void combine_satf(i_fcolor *, i_fcolor *, int, int);
951 static void combine_value(i_color *, i_color *, int, int);
952 static void combine_valuef(i_fcolor *, i_fcolor *, int, int);
953 static void combine_color(i_color *, i_color *, int, int);
954 static void combine_colorf(i_fcolor *, i_fcolor *, int, int);
955
956 static struct i_combines {
957   i_fill_combine_f combine;
958   i_fill_combinef_f combinef;
959 } combines[] =
960 {
961   { /* replace */
962     combine_replace,
963     combine_replacef,
964   },
965   { /* alpha blend */
966     combine_alphablend,
967     combine_alphablendf,
968   },
969   {
970     /* multiply */
971     combine_mult,
972     combine_multf,
973   },
974   {
975     /* dissolve */
976     combine_dissolve,
977     combine_dissolvef,
978   },
979   {
980     /* add */
981     combine_add,
982     combine_addf,
983   },
984   {
985     /* subtract */
986     combine_subtract,
987     combine_subtractf,
988   },
989   {
990     /* diff */
991     combine_diff,
992     combine_difff,
993   },
994   {
995     combine_lighten,
996     combine_lightenf,
997   },
998   {
999     combine_darken,
1000     combine_darkenf,
1001   },
1002   {
1003     combine_hue,
1004     combine_huef,
1005   },
1006   {
1007     combine_sat,
1008     combine_satf,
1009   },
1010   {
1011     combine_value,
1012     combine_valuef,
1013   },
1014   {
1015     combine_color,
1016     combine_colorf,
1017   },
1018 };
1019
1020 /*
1021 =item i_get_combine(combine, color_func, fcolor_func)
1022
1023 =cut
1024 */
1025
1026 void i_get_combine(int combine, i_fill_combine_f *color_func, 
1027                    i_fill_combinef_f *fcolor_func) {
1028   if (combine < 0 || combine > sizeof(combines) / sizeof(*combines))
1029     combine = 0;
1030
1031   *color_func = combines[combine].combine;
1032   *fcolor_func = combines[combine].combinef;
1033 }
1034
1035 static void combine_replace(i_color *out, i_color *in, int channels, int count) {
1036   while (count--) {
1037     *out++ = *in++;
1038   }
1039 }
1040
1041 static void combine_replacef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1042   while (count--) {
1043     *out++ = *in++;
1044   }
1045 }
1046
1047 static void combine_alphablend(i_color *out, i_color *in, int channels, int count) {
1048   while (count--) {
1049     COMBINE(*out, *in, channels);
1050     ++out;
1051     ++in;
1052   }
1053 }
1054
1055 static void combine_alphablendf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1056   while (count--) {
1057     COMBINEF(*out, *in, channels);
1058     ++out;
1059     ++in;
1060   }
1061 }
1062
1063 static void combine_mult(i_color *out, i_color *in, int channels, int count) {
1064   int ch;
1065
1066   while (count--) {
1067     double mult[MAXCHANNELS];
1068     mult[3] = in->channel[3];
1069     for (ch = 0; ch < (channels); ++ch) { 
1070       if (ch != 3)
1071         mult[ch] = (out->channel[ch] * in->channel[ch]) * (1.0 / 255);
1072     } 
1073     COMBINEA(*out, mult, channels);
1074     ++out;
1075     ++in;
1076   }
1077 }
1078
1079 static void combine_multf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1080   int ch;
1081
1082   while (count--) {
1083     i_fcolor c = *in;
1084     for (ch = 0; ch < channels; ++ch) { 
1085       if (ch != 3)
1086         c.channel[ch] = out->channel[ch] * in->channel[ch];
1087     } 
1088     COMBINEF(*out, c, channels);
1089     ++out;
1090     ++in;
1091   }
1092 }
1093
1094 static void combine_dissolve(i_color *out, i_color *in, int channels, int count) {
1095   while (count--) {
1096     if (in->channel[3] > rand() * (255.0 / RAND_MAX))
1097       COMBINE(*out, *in, channels);
1098     ++out;
1099     ++in;
1100   }
1101 }
1102
1103 static void combine_dissolvef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1104   while (count--) {
1105     if (in->channel[3] > rand() * (1.0 / RAND_MAX))
1106       COMBINEF(*out, *in, channels);
1107     ++out;
1108     ++in;
1109   }
1110 }
1111
1112 static void combine_add(i_color *out, i_color *in, int channels, int count) {
1113   int ch;
1114
1115   while (count--) {
1116     i_color c = *in;
1117     for (ch = 0; ch < (channels); ++ch) { 
1118       if (ch != 3) {
1119         int total = out->channel[ch] + in->channel[ch];
1120         if (total > 255)
1121           total = 255;
1122         c.channel[ch] = total;
1123       }
1124     } 
1125     COMBINE(*out, c, channels);
1126     ++out;
1127     ++in;
1128   }
1129 }
1130
1131 static void combine_addf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1132   int ch;
1133
1134   while (count--) {
1135     i_fcolor c = *in;
1136     for (ch = 0; ch < (channels); ++ch) { 
1137       if (ch != 3) {
1138         double total = out->channel[ch] + in->channel[ch];
1139         if (total > 1.0)
1140           total = 1.0;
1141         out->channel[ch] = total;
1142       }
1143     } 
1144     COMBINEF(*out, c, channels);
1145     ++out;
1146     ++in;
1147   }
1148 }
1149
1150 static void combine_subtract(i_color *out, i_color *in, int channels, int count) {
1151   int ch;
1152
1153   while (count--) {
1154     i_color c = *in;
1155     for (ch = 0; ch < (channels); ++ch) { 
1156       if (ch != 3) {
1157         int total = out->channel[ch] - in->channel[ch];
1158         if (total < 0)
1159           total = 0;
1160         c.channel[ch] = total;
1161       }
1162     } 
1163     COMBINE(*out, c, channels);
1164     ++out;
1165     ++in;
1166   }
1167 }
1168
1169 static void combine_subtractf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1170   int ch;
1171
1172   while (count--) {
1173     i_fcolor c = *in;
1174     for (ch = 0; ch < channels; ++ch) { 
1175       if (ch != 3) {
1176         double total = out->channel[ch] - in->channel[ch];
1177         if (total < 0)
1178           total = 0;
1179         c.channel[ch] = total;
1180       }
1181     } 
1182     COMBINEF(*out, c, channels);
1183     ++out;
1184     ++in;
1185   }
1186 }
1187
1188 static void combine_diff(i_color *out, i_color *in, int channels, int count) {
1189   int ch;
1190
1191   while (count--) {
1192     i_color c = *in;
1193     for (ch = 0; ch < (channels); ++ch) { 
1194       if (ch != 3) 
1195         c.channel[ch] = abs(out->channel[ch] - in->channel[ch]);
1196     } 
1197     COMBINE(*out, c, channels)
1198     ++out;
1199     ++in;
1200   }
1201 }
1202
1203 static void combine_difff(i_fcolor *out, i_fcolor *in, int channels, int count) {
1204   int ch;
1205
1206   while (count--) {
1207     i_fcolor c = *in;
1208     for (ch = 0; ch < (channels); ++ch) { 
1209       if (ch != 3)
1210         c.channel[ch] = fabs(out->channel[ch] - in->channel[ch]);
1211     }
1212     COMBINEF(*out, c, channels);
1213     ++out;
1214     ++in;
1215   }
1216 }
1217
1218 static void combine_darken(i_color *out, i_color *in, int channels, int count) {
1219   int ch;
1220
1221   while (count--) {
1222     for (ch = 0; ch < channels; ++ch) { 
1223       if (ch != 3 && out->channel[ch] < in->channel[ch])
1224         in->channel[ch] = out->channel[ch];
1225     } 
1226     COMBINE(*out, *in, channels);
1227     ++out;
1228     ++in;
1229   }
1230 }
1231
1232 static void combine_darkenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1233   int ch;
1234
1235   while (count--) {
1236     for (ch = 0; ch < channels; ++ch) { 
1237       if (ch != 3 && out->channel[ch] < in->channel[ch])
1238         in->channel[ch] = out->channel[ch];
1239     } 
1240     COMBINEF(*out, *in, channels);
1241     ++out;
1242     ++in;
1243   }
1244 }
1245
1246 static void combine_lighten(i_color *out, i_color *in, int channels, int count) {
1247   int ch;
1248
1249   while (count--) {
1250     for (ch = 0; ch < channels; ++ch) { 
1251       if (ch != 3 && out->channel[ch] > in->channel[ch])
1252         in->channel[ch] = out->channel[ch];
1253     } 
1254     COMBINE(*out, *in, channels);
1255     ++out;
1256     ++in;
1257   }
1258 }
1259
1260 static void combine_lightenf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1261   int ch;
1262
1263   while (count--) {
1264     for (ch = 0; ch < channels; ++ch) { 
1265       if (ch != 3 && out->channel[ch] > in->channel[ch])
1266         in->channel[ch] = out->channel[ch];
1267     } 
1268     COMBINEF(*out, *in, channels);
1269     ++out;
1270     ++in;
1271   }
1272 }
1273
1274 static void combine_hue(i_color *out, i_color *in, int channels, int count) {
1275   while (count--) {
1276     i_color c = *out;
1277     i_rgb_to_hsv(&c);
1278     i_rgb_to_hsv(in);
1279     c.channel[0] = in->channel[0];
1280     i_hsv_to_rgb(&c);
1281     c.channel[3] = in->channel[3];
1282     COMBINE(*out, c, channels);
1283     ++out;
1284     ++in;
1285   }
1286 }
1287
1288 static void combine_huef(i_fcolor *out, i_fcolor *in, int channels, int count) {
1289   while (count--) {
1290     i_fcolor c = *out;
1291     i_rgb_to_hsvf(&c);
1292     i_rgb_to_hsvf(in);
1293     c.channel[0] = in->channel[0];
1294     i_hsv_to_rgbf(&c);
1295     c.channel[3] = in->channel[3];
1296     COMBINEF(*out, c, channels);
1297     ++out;
1298     ++in;
1299   }
1300 }
1301
1302 static void combine_sat(i_color *out, i_color *in, int channels, int count) {
1303   while (count--) {
1304     i_color c = *out;
1305     i_rgb_to_hsv(&c);
1306     i_rgb_to_hsv(in);
1307     c.channel[1] = in->channel[1];
1308     i_hsv_to_rgb(&c);
1309     c.channel[3] = in->channel[3];
1310     COMBINE(*out, c, channels);
1311     ++out;
1312     ++in;
1313   }
1314 }
1315
1316 static void combine_satf(i_fcolor *out, i_fcolor *in, int channels, int count) {
1317   while (count--) {
1318     i_fcolor c = *out;
1319     i_rgb_to_hsvf(&c);
1320     i_rgb_to_hsvf(in);
1321     c.channel[1] = in->channel[1];
1322     i_hsv_to_rgbf(&c);
1323     c.channel[3] = in->channel[3];
1324     COMBINEF(*out, c, channels);
1325     ++out;
1326     ++in;
1327   }
1328 }
1329
1330 static void combine_value(i_color *out, i_color *in, int channels, int count) {
1331   while (count--) {
1332     i_color c = *out;
1333     i_rgb_to_hsv(&c);
1334     i_rgb_to_hsv(in);
1335     c.channel[2] = in->channel[2];
1336     i_hsv_to_rgb(&c);
1337     c.channel[3] = in->channel[3];
1338     COMBINE(*out, c, channels);
1339     ++out;
1340     ++in;
1341   }
1342 }
1343
1344 static void combine_valuef(i_fcolor *out, i_fcolor *in, int channels, 
1345                            int count) {
1346   while (count--) {
1347     i_fcolor c = *out;
1348     i_rgb_to_hsvf(&c);
1349     i_rgb_to_hsvf(in);
1350     c.channel[2] = in->channel[2];
1351     i_hsv_to_rgbf(&c);
1352     c.channel[3] = in->channel[3];
1353     COMBINEF(*out, c, channels);
1354     ++out;
1355     ++in;
1356   }
1357 }
1358
1359 static void combine_color(i_color *out, i_color *in, int channels, int count) {
1360   while (count--) {
1361     i_color c = *out;
1362     i_rgb_to_hsv(&c);
1363     i_rgb_to_hsv(in);
1364     c.channel[0] = in->channel[0];
1365     c.channel[1] = in->channel[1];
1366     i_hsv_to_rgb(&c);
1367     c.channel[3] = in->channel[3];
1368     COMBINE(*out, c, channels);
1369     ++out;
1370     ++in;
1371   }
1372 }
1373
1374 static void combine_colorf(i_fcolor *out, i_fcolor *in, int channels, 
1375                            int count) {
1376   while (count--) {
1377     i_fcolor c = *out;
1378     i_rgb_to_hsvf(&c);
1379     i_rgb_to_hsvf(in);
1380     c.channel[0] = in->channel[0];
1381     c.channel[1] = in->channel[1];
1382     i_hsv_to_rgbf(&c);
1383     c.channel[3] = in->channel[3];
1384     COMBINEF(*out, c, channels);
1385     ++out;
1386     ++in;
1387   }
1388 }
1389
1390
1391 /*
1392 =back
1393
1394 =head1 AUTHOR
1395
1396 Tony Cook <tony@develop-help.com>
1397
1398 =head1 SEE ALSO
1399
1400 Imager(3)
1401
1402 =cut
1403 */