commit changes from draw branch
[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
170 static i_fill_solid_t base_solid_fill =
171 {
172   {
173     fill_solid,
174     fill_solidf,
175     NULL,
176     NULL,
177     NULL,
178   },
179 };
180
181 /*
182 =item i_fill_destroy(fill)
183
184 =category Fills
185 =synopsis i_fill_destroy(fill);
186
187 Call to destroy any fill object.
188
189 =cut
190 */
191
192 void
193 i_fill_destroy(i_fill_t *fill) {
194   if (fill->destroy)
195     (fill->destroy)(fill);
196   myfree(fill);
197 }
198
199 /*
200 =item i_new_fill_solidf(color, combine)
201
202 =category Fills
203 =synopsis i_fill_t *fill = i_new_fill_solidf(&fcolor, combine);
204
205 Create a solid fill based on a float color.
206
207 If combine is non-zero then alpha values will be combined.
208
209 =cut
210 */
211
212 i_fill_t *
213 i_new_fill_solidf(const i_fcolor *c, int combine) {
214   int ch;
215   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
216   
217   *fill = base_solid_fill;
218   if (combine) {
219     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
220   }
221
222   fill->fc = *c;
223   for (ch = 0; ch < MAXCHANNELS; ++ch) {
224     fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
225   }
226   
227   return &fill->base;
228 }
229
230 /*
231 =item i_new_fill_solid(color, combine)
232
233 =category Fills
234 =synopsis i_fill_t *fill = i_new_fill_solid(&color, combine);
235
236 Create a solid fill based on an 8-bit color.
237
238 If combine is non-zero then alpha values will be combined.
239
240 =cut
241 */
242
243 i_fill_t *
244 i_new_fill_solid(const i_color *c, int combine) {
245   int ch;
246   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
247
248   *fill = base_solid_fill;
249   if (combine) {
250     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
251   }
252
253   fill->c = *c;
254   for (ch = 0; ch < MAXCHANNELS; ++ch) {
255     fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
256   }
257   
258   return &fill->base;
259 }
260
261 static unsigned char
262 builtin_hatches[][8] =
263 {
264   {
265     /* 1x1 checkerboard */
266     0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
267   },
268   {
269     /* 2x2 checkerboard */
270     0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33,
271   },
272   {
273     /* 4 x 4 checkerboard */
274     0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
275   },
276   {
277     /* single vertical lines */
278     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
279   },
280   {
281     /* double vertical lines */
282     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 
283   },
284   {
285     /* quad vertical lines */
286     0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
287   },
288   {
289     /* single hlines */
290     0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291   },
292   {
293     /* double hlines */
294     0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
295   },
296   {
297     /* quad hlines */
298     0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
299   },
300   {
301     /* single / */
302     0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
303   },
304   {
305     /* single \ */
306     0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
307   },
308   {
309     /* double / */
310     0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
311   },
312   {
313     /* double \ */
314     0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11,
315   },
316   {
317     /* single grid */
318     0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
319   },
320   {
321     /* double grid */
322     0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88,
323   },
324   {
325     /* quad grid */
326     0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA,
327   },
328   {
329     /* single dots */
330     0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331   },
332   {
333     /* 4 dots */
334     0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
335   },
336   {
337     /* 16 dots */
338     0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
339   },
340   {
341     /* simple stipple */
342     0x48, 0x84, 0x00, 0x00, 0x84, 0x48, 0x00, 0x00,
343   },
344   {
345     /* weave */
346     0x55, 0xFD, 0x05, 0xFD, 0x55, 0xDF, 0x50, 0xDF,
347   },
348   {
349     /* single cross hatch */
350     0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x01,
351   },
352   {
353     /* double cross hatch */
354     0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11,
355   },
356   {
357     /* vertical lozenge */
358     0x11, 0x11, 0x11, 0xAA, 0x44, 0x44, 0x44, 0xAA,
359   },
360   {
361     /* horizontal lozenge */
362     0x88, 0x70, 0x88, 0x07, 0x88, 0x70, 0x88, 0x07,
363   },
364   {
365     /* scales overlapping downwards */
366     0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3,
367   },
368   {
369     /* scales overlapping upwards */
370     0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01,
371   },
372   {
373     /* scales overlapping leftwards */
374     0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84,
375   },
376   {
377     /* scales overlapping rightwards */
378     0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1,
379   },
380   {
381     /* denser stipple */
382     0x44, 0x88, 0x22, 0x11, 0x44, 0x88, 0x22, 0x11,
383   },
384   {
385     /* L-shaped tiles */
386     0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
387   },
388   {
389     /* wider stipple */
390     0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
391   },
392 };
393
394 typedef struct
395 {
396   i_fill_t base;
397   i_color fg, bg;
398   i_fcolor ffg, fbg;
399   unsigned char hatch[8];
400   int dx, dy;
401 } i_fill_hatch_t;
402
403 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
404                        i_color *data);
405 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
406                         i_fcolor *data);
407 static
408 i_fill_t *
409 i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const i_fcolor *fbg, 
410                 int combine, int hatch, const unsigned char *cust_hatch,
411                 int dx, int dy);
412
413 /*
414 =item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
415
416 =category Fills
417 =synopsis i_fill_t *fill = i_new_fill_hatch(&fg_color, &bg_color, combine, hatch, custom_hatch, dx, dy);
418
419 Creates a new hatched fill with the fg color used for the 1 bits in
420 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
421 will be combined.
422
423 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
424 hash definition, with the high-bits to the left.
425
426 If cust_hatch is NULL then one of the standard hatches is used.
427
428 (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.
429
430 =cut
431 */
432 i_fill_t *
433 i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch, 
434             const unsigned char *cust_hatch, int dx, int dy) {
435   return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, 
436                          dx, dy);
437 }
438
439 /*
440 =item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
441
442 =category Fills
443 =synopsis i_fill_t *fill = i_new_fill_hatchf(&fg_fcolor, &bg_fcolor, combine, hatch, custom_hatch, dx, dy);
444
445 Creates a new hatched fill with the fg color used for the 1 bits in
446 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
447 will be combined.
448
449 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
450 hash definition, with the high-bits to the left.
451
452 If cust_hatch is NULL then one of the standard hatches is used.
453
454 (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.
455
456 =cut
457 */
458 i_fill_t *
459 i_new_fill_hatchf(const i_fcolor *fg, const i_fcolor *bg, int combine, int hatch, 
460                   const unsigned char *cust_hatch, int dx, int dy) {
461   return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, 
462                          dx, dy);
463 }
464
465 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
466                        i_color *data);
467 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
468                        i_fcolor *data);
469 struct i_fill_image_t {
470   i_fill_t base;
471   i_img *src;
472   int xoff, yoff;
473   int has_matrix;
474   double matrix[9];
475 };
476
477 static struct i_fill_image_t
478 image_fill_proto =
479   {
480     {
481       fill_image,
482       fill_imagef,
483       NULL
484     }
485   };
486
487 /*
488 =item i_new_fill_image(im, matrix, xoff, yoff, combine)
489
490 =category Fills
491 =synopsis i_fill_t *fill = i_new_fill_image(src_img, matrix, x_offset, y_offset, combine);
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 = image_fill_proto;
506
507   if (combine) {
508     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
509   }
510   else {
511     fill->base.combine = NULL;
512     fill->base.combinef = NULL;
513   }
514   fill->src = im;
515   if (xoff < 0)
516     xoff += im->xsize;
517   fill->xoff = xoff;
518   if (yoff < 0)
519     yoff += im->ysize;
520   fill->yoff = yoff;
521   if (matrix) {
522     fill->has_matrix = 1;
523     memcpy(fill->matrix, matrix, sizeof(fill->matrix));
524   }
525   else
526     fill->has_matrix = 0;
527
528   return &fill->base;
529 }
530
531
532 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
533
534 /*
535 =back
536
537 =head1 INTERNAL FUNCTIONS
538
539 =over
540
541 =item fill_solid(fill, x, y, width, channels, data)
542
543 The 8-bit sample fill function for non-combining solid fills.
544
545 =cut
546 */
547 static void
548 fill_solid(i_fill_t *fill, int x, int y, int width, int channels, 
549            i_color *data) {
550   i_color c = T_SOLID_FILL(fill)->c;
551   i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
552   while (width-- > 0) {
553     *data++ = 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   i_fcolor c = T_SOLID_FILL(fill)->fc;
568   i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
569   while (width-- > 0) {
570     *data++ = c;
571   }
572 }
573
574 static i_fill_hatch_t
575 hatch_fill_proto =
576   {
577     {
578       fill_hatch,
579       fill_hatchf,
580       NULL
581     }
582   };
583
584 /*
585 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
586
587 Implements creation of hatch fill objects.
588
589 =cut
590 */
591 static
592 i_fill_t *
593 i_new_hatch_low(const i_color *fg, const i_color *bg, 
594                 const i_fcolor *ffg, const i_fcolor *fbg, 
595                 int combine, int hatch, const unsigned char *cust_hatch,
596                 int dx, int dy) {
597   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
598
599   *fill = hatch_fill_proto;
600   /* Some Sun C didn't like the condition expressions that were here.
601      See https://rt.cpan.org/Ticket/Display.html?id=21944
602    */
603   if (fg)
604     fill->fg = *fg;
605   else
606     fill->fg = fcolor_to_color(ffg);
607   if (bg)
608     fill->bg = *bg;
609   else
610     fill->bg = fcolor_to_color(fbg);
611   if (ffg) 
612     fill->ffg = *ffg;
613   else
614     fill->ffg = color_to_fcolor(fg);
615   if (fbg)
616     fill->fbg = *fbg;
617   else
618     fill->fbg = color_to_fcolor(bg);
619   if (combine) {
620     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
621   }
622   else {
623     fill->base.combine = NULL;
624     fill->base.combinef = NULL;
625   }
626   if (cust_hatch) {
627     memcpy(fill->hatch, cust_hatch, 8);
628   }
629   else {
630     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
631       hatch = 0;
632     memcpy(fill->hatch, builtin_hatches[hatch], 8);
633   }
634   fill->dx = dx & 7;
635   fill->dy = dy & 7;
636
637   return &fill->base;
638 }
639
640 /*
641 =item fill_hatch(fill, x, y, width, channels, data)
642
643 The 8-bit sample fill function for hatched fills.
644
645 =cut
646 */
647 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
648                        i_color *data) {
649   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
650   int byte = f->hatch[(y + f->dy) & 7];
651   int xpos = (x + f->dx) & 7;
652   int mask = 128 >> xpos;
653
654   while (width-- > 0) {
655     if (byte & mask)
656       *data++ = f->fg;
657     else
658       *data++ = f->bg;
659     
660     if ((mask >>= 1) == 0)
661       mask = 128;
662   }
663 }
664
665 /*
666 =item fill_hatchf(fill, x, y, width, channels, data)
667
668 The floating sample fill function for hatched fills.
669
670 =back
671 */
672 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
673                         i_fcolor *data) {
674   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
675   int byte = f->hatch[(y + f->dy) & 7];
676   int xpos = (x + f->dx) & 7;
677   int mask = 128 >> xpos;
678   
679   while (width-- > 0) {
680     if (byte & mask)
681       *data++ = f->ffg;
682     else
683       *data++ = f->fbg;
684     
685     if ((mask >>= 1) == 0)
686       mask = 128;
687   }
688 }
689
690 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
691 /* linear interpolation */
692 static i_color interp_i_color(i_color before, i_color after, double pos,
693                               int channels) {
694   i_color out;
695   int ch;
696
697   pos -= floor(pos);
698   for (ch = 0; ch < channels; ++ch)
699     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
700   if (channels > 3 && out.channel[3])
701     for (ch = 0; ch < channels; ++ch)
702       if (ch != 3) {
703         int temp = out.channel[ch] * 255 / out.channel[3];
704         if (temp > 255)
705           temp = 255;
706         out.channel[ch] = temp;
707       }
708
709   return out;
710 }
711
712 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
713 /* linear interpolation */
714 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
715                                 int channels) {
716   i_fcolor out;
717   int ch;
718
719   pos -= floor(pos);
720   for (ch = 0; ch < channels; ++ch)
721     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
722   if (out.channel[3])
723     for (ch = 0; ch < channels; ++ch)
724       if (ch != 3) {
725         int temp = out.channel[ch] / out.channel[3];
726         if (temp > 1.0)
727           temp = 1.0;
728         out.channel[ch] = temp;
729       }
730
731   return out;
732 }
733
734 /*
735 =item fill_image(fill, x, y, width, channels, data, work)
736
737 =cut
738 */
739 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
740                        i_color *data) {
741   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
742   int i = 0;
743   i_color *out = data;
744   
745   if (f->has_matrix) {
746     /* the hard way */
747     while (i < width) {
748       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
749       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
750       double ix = floor(rx / f->src->xsize);
751       double iy = floor(ry / f->src->ysize);
752       i_color c[2][2];
753       i_color c2[2];
754       int dy;
755
756       if (f->xoff) {
757         rx += iy * f->xoff;
758         ix = floor(rx / f->src->xsize);
759       }
760       else if (f->yoff) {
761         ry += ix * f->yoff;
762         iy = floor(ry / f->src->ysize);
763       }
764       rx -= ix * f->src->xsize;
765       ry -= iy * f->src->ysize;
766
767       for (dy = 0; dy < 2; ++dy) {
768         if ((int)rx == f->src->xsize-1) {
769           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
770           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
771         }
772         else {
773           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
774                  c[dy]);
775         }
776         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
777       }
778       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
779       ++i;
780     }
781   }
782   else {
783     /* the easy way */
784     /* this should be possible to optimize to use i_glin() */
785     while (i < width) {
786       int rx = x+i;
787       int ry = y;
788       int ix = rx / f->src->xsize;
789       int iy = ry / f->src->ysize;
790
791       if (f->xoff) {
792         rx += iy * f->xoff;
793         ix = rx / f->src->xsize;
794       }
795       else if (f->yoff) {
796         ry += ix * f->yoff;
797         iy = ry / f->src->xsize;
798       }
799       rx -= ix * f->src->xsize;
800       ry -= iy * f->src->ysize;
801       i_gpix(f->src, rx, ry, out);
802       ++out;
803       ++i;
804     }
805   }
806   if (f->src->channels == 3) {
807     /* just set the alpha */
808     for (i = 0; i <  width; ++i) {
809       data->channel[3] = 255;
810       data++;
811     }
812   }
813   else if (f->src->channels == 2) {
814     /* copy the alpha to channel 3, duplicate the grey value */
815     for (i = 0; i <  width; ++i) {
816       data->channel[3] = data->channel[1];
817       data->channel[1] = data->channel[2] = data->channel[0];
818       data++;
819     }
820   }
821   else if (f->src->channels == 1) {
822     /* set the alpha, duplicate grey */
823     for (i = 0; i <  width; ++i) {
824       data->channel[3] = 255;
825       data->channel[1] = data->channel[2] = data->channel[0];
826       data++;
827     }
828   }
829 }
830
831 /*
832 =item fill_image(fill, x, y, width, channels, data, work)
833
834 =cut
835 */
836 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
837                        i_fcolor *data) {
838   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
839   int i = 0;
840   
841   if (f->has_matrix) {
842     /* the hard way */
843     while (i < width) {
844       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
845       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
846       double ix = floor(rx / f->src->xsize);
847       double iy = floor(ry / f->src->ysize);
848       i_fcolor c[2][2];
849       i_fcolor c2[2];
850       int dy;
851
852       if (f->xoff) {
853         rx += iy * f->xoff;
854         ix = floor(rx / f->src->xsize);
855       }
856       else if (f->yoff) {
857         ry += ix * f->yoff;
858         iy = floor(ry / f->src->ysize);
859       }
860       rx -= ix * f->src->xsize;
861       ry -= iy * f->src->ysize;
862
863       for (dy = 0; dy < 2; ++dy) {
864         if ((int)rx == f->src->xsize-1) {
865           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
866           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
867         }
868         else {
869           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
870                  c[dy]);
871         }
872         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
873       }
874       *data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
875       ++i;
876     }
877   }
878   else {
879     /* the easy way */
880     /* this should be possible to optimize to use i_glin() */
881     while (i < width) {
882       int rx = x+i;
883       int ry = y;
884       int ix = rx / f->src->xsize;
885       int iy = ry / f->src->ysize;
886
887       if (f->xoff) {
888         rx += iy * f->xoff;
889         ix = rx / f->src->xsize;
890       }
891       else if (f->yoff) {
892         ry += ix * f->yoff;
893         iy = ry / f->src->xsize;
894       }
895       rx -= ix * f->src->xsize;
896       ry -= iy * f->src->ysize;
897       i_gpixf(f->src, rx, ry, data);
898       ++data;
899       ++i;
900     }
901   }
902   if (f->src->channels == 3) {
903     /* just set the alpha */
904     for (i = 0; i <  width; ++i) {
905       data->channel[3] = 1.0;
906       data++;
907     }
908   }
909   else if (f->src->channels == 2) {
910     /* copy the alpha to channel 3, duplicate the grey value */
911     for (i = 0; i <  width; ++i) {
912       data->channel[3] = data->channel[1];
913       data->channel[1] = data->channel[2] = data->channel[0];
914       data++;
915     }
916   }
917   else if (f->src->channels == 1) {
918     /* set the alpha, duplicate grey */
919     for (i = 0; i <  width; ++i) {
920       data->channel[3] = 1.0;
921       data->channel[1] = data->channel[2] = data->channel[0];
922       data++;
923     }
924   }
925 }
926
927
928 /*
929 =back
930
931 =head1 AUTHOR
932
933 Tony Cook <tony@develop-help.com>
934
935 =head1 SEE ALSO
936
937 Imager(3)
938
939 =cut
940 */