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