]> git.imager.perl.org - imager.git/blob - fills.c
make sure flipping paletted images is covered
[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   fill = i_new_fill_opacity(fill, alpha_mult);
21   i_fill_destroy(fill);
22
23 =head1 DESCRIPTION
24
25 Implements the basic general fills, which can be used for filling some
26 shapes and for flood fills.
27
28 Each fill can implement up to 3 functions:
29
30 =over
31
32 =item fill_with_color
33
34 called for fills on 8-bit images.  This can be NULL in which case the
35 fill_with_colorf function is called.
36
37 =item fill_with_fcolor
38
39 called for fills on non-8-bit images or when fill_with_color is NULL.
40
41 =item destroy
42
43 called by i_fill_destroy() if non-NULL, to release any extra resources
44 that the fill may need.
45
46 =back
47
48 fill_with_color and fill_with_fcolor are basically the same function
49 except that the first works with lines of i_color and the second with
50 lines of i_fcolor.
51
52 If the combines member if non-zero the line data is populated from the
53 target image before calling fill_with_*color.
54
55 fill_with_color needs to fill the I<data> parameter with the fill
56 pixels.  If combines is non-zero it the fill pixels should be combined
57 with the existing data.
58
59 The current fills are:
60
61 =over
62
63 =item *
64
65 solid fill
66
67 =item *
68
69 hatched fill
70
71 =item *
72
73 fountain fill
74
75 =back
76
77 Fountain fill is implemented by L<filters.c>.
78
79 Other fills that could be implemented include:
80
81 =over
82
83 =item *
84
85 image - an image tiled over the fill area, with an offset either
86 horizontally or vertically.
87
88 =item *
89
90 checkerboard - combine 2 fills in a checkerboard
91
92 =item *
93
94 combine - combine the levels of 2 other fills based in the levels of
95 an image
96
97 =item *
98
99 regmach - use the register machine to generate colors
100
101 =back
102
103 =over
104
105 =cut
106 */
107
108 static i_color fcolor_to_color(const i_fcolor *c) {
109   int ch;
110   i_color out;
111
112   for (ch = 0; ch < MAXCHANNELS; ++ch)
113     out.channel[ch] = SampleFTo8(c->channel[ch]);
114
115   return out;
116 }
117
118 static i_fcolor color_to_fcolor(const i_color *c) {
119   int ch;
120   i_fcolor out;
121
122   for (ch = 0; ch < MAXCHANNELS; ++ch)
123     out.channel[ch] = Sample8ToF(c->channel[ch]);
124
125   return out;
126 }
127
128 /* alpha combine in with out */
129 #define COMBINE(out, in, channels) \
130   { \
131     int ch; \
132     for (ch = 0; ch < (channels); ++ch) { \
133       (out).channel[ch] = ((out).channel[ch] * (255 - (in).channel[3]) \
134         + (in).channel[ch] * (in).channel[3]) / 255; \
135     } \
136   }
137
138 /* alpha combine in with out, in this case in is a simple array of
139    samples, potentially not integers - the mult combiner uses doubles
140    for accuracy */
141 #define COMBINEA(out, in, channels) \
142   { \
143     int ch; \
144     for (ch = 0; ch < (channels); ++ch) { \
145       (out).channel[ch] = ((out).channel[ch] * (255 - (in)[3]) \
146         + (in)[ch] * (in)[3]) / 255; \
147     } \
148   }
149
150 #define COMBINEF(out, in, channels) \
151   { \
152     int ch; \
153     for (ch = 0; ch < (channels); ++ch) { \
154       (out).channel[ch] = (out).channel[ch] * (1.0 - (in).channel[3]) \
155         + (in).channel[ch] * (in).channel[3]; \
156     } \
157   }
158
159 typedef struct
160 {
161   i_fill_t base;
162   i_color c;
163   i_fcolor fc;
164 } i_fill_solid_t;
165
166 static void fill_solid(i_fill_t *, int x, int y, int width, int channels, 
167                        i_color *);
168 static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, 
169                         i_fcolor *);
170
171 static i_fill_solid_t base_solid_fill =
172 {
173   {
174     fill_solid,
175     fill_solidf,
176     NULL,
177     NULL,
178     NULL,
179   },
180 };
181
182 /*
183 =item i_fill_destroy(fill)
184 =order 90
185 =category Fills
186 =synopsis i_fill_destroy(fill);
187
188 Call to destroy any fill object.
189
190 =cut
191 */
192
193 void
194 i_fill_destroy(i_fill_t *fill) {
195   if (fill->destroy)
196     (fill->destroy)(fill);
197   myfree(fill);
198 }
199
200 /*
201 =item i_new_fill_solidf(color, combine)
202
203 =category Fills
204 =synopsis i_fill_t *fill = i_new_fill_solidf(&fcolor, combine);
205
206 Create a solid fill based on a float color.
207
208 If combine is non-zero then alpha values will be combined.
209
210 =cut
211 */
212
213 i_fill_t *
214 i_new_fill_solidf(const i_fcolor *c, int combine) {
215   int ch;
216   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
217   
218   *fill = base_solid_fill;
219   if (combine) {
220     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
221   }
222
223   fill->fc = *c;
224   for (ch = 0; ch < MAXCHANNELS; ++ch) {
225     fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
226   }
227   
228   return &fill->base;
229 }
230
231 /*
232 =item i_new_fill_solid(color, combine)
233
234 =category Fills
235 =synopsis i_fill_t *fill = i_new_fill_solid(&color, combine);
236
237 Create a solid fill based on an 8-bit color.
238
239 If combine is non-zero then alpha values will be combined.
240
241 =cut
242 */
243
244 i_fill_t *
245 i_new_fill_solid(const i_color *c, int combine) {
246   int ch;
247   i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t)); /* checked 14jul05 tonyc */
248
249   *fill = base_solid_fill;
250   if (combine) {
251     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
252   }
253
254   fill->c = *c;
255   for (ch = 0; ch < MAXCHANNELS; ++ch) {
256     fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
257   }
258   
259   return &fill->base;
260 }
261
262 static unsigned char
263 builtin_hatches[][8] =
264 {
265   {
266     /* 1x1 checkerboard */
267     0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
268   },
269   {
270     /* 2x2 checkerboard */
271     0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33,
272   },
273   {
274     /* 4 x 4 checkerboard */
275     0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
276   },
277   {
278     /* single vertical lines */
279     0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
280   },
281   {
282     /* double vertical lines */
283     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 
284   },
285   {
286     /* quad vertical lines */
287     0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
288   },
289   {
290     /* single hlines */
291     0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292   },
293   {
294     /* double hlines */
295     0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
296   },
297   {
298     /* quad hlines */
299     0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
300   },
301   {
302     /* single / */
303     0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
304   },
305   {
306     /* single \ */
307     0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
308   },
309   {
310     /* double / */
311     0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
312   },
313   {
314     /* double \ */
315     0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11,
316   },
317   {
318     /* single grid */
319     0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
320   },
321   {
322     /* double grid */
323     0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88,
324   },
325   {
326     /* quad grid */
327     0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA,
328   },
329   {
330     /* single dots */
331     0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332   },
333   {
334     /* 4 dots */
335     0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
336   },
337   {
338     /* 16 dots */
339     0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
340   },
341   {
342     /* simple stipple */
343     0x48, 0x84, 0x00, 0x00, 0x84, 0x48, 0x00, 0x00,
344   },
345   {
346     /* weave */
347     0x55, 0xFD, 0x05, 0xFD, 0x55, 0xDF, 0x50, 0xDF,
348   },
349   {
350     /* single cross hatch */
351     0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x01,
352   },
353   {
354     /* double cross hatch */
355     0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11,
356   },
357   {
358     /* vertical lozenge */
359     0x11, 0x11, 0x11, 0xAA, 0x44, 0x44, 0x44, 0xAA,
360   },
361   {
362     /* horizontal lozenge */
363     0x88, 0x70, 0x88, 0x07, 0x88, 0x70, 0x88, 0x07,
364   },
365   {
366     /* scales overlapping downwards */
367     0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3,
368   },
369   {
370     /* scales overlapping upwards */
371     0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01,
372   },
373   {
374     /* scales overlapping leftwards */
375     0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84,
376   },
377   {
378     /* scales overlapping rightwards */
379     0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1,
380   },
381   {
382     /* denser stipple */
383     0x44, 0x88, 0x22, 0x11, 0x44, 0x88, 0x22, 0x11,
384   },
385   {
386     /* L-shaped tiles */
387     0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
388   },
389   {
390     /* wider stipple */
391     0x80, 0x40, 0x20, 0x00, 0x02, 0x04, 0x08, 0x00,
392   },
393 };
394
395 typedef struct
396 {
397   i_fill_t base;
398   i_color fg, bg;
399   i_fcolor ffg, fbg;
400   unsigned char hatch[8];
401   int dx, dy;
402 } i_fill_hatch_t;
403
404 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
405                        i_color *data);
406 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
407                         i_fcolor *data);
408 static
409 i_fill_t *
410 i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const i_fcolor *fbg, 
411                 int combine, int hatch, const unsigned char *cust_hatch,
412                 int dx, int dy);
413
414 /*
415 =item i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
416
417 =category Fills
418 =synopsis i_fill_t *fill = i_new_fill_hatch(&fg_color, &bg_color, combine, hatch, custom_hatch, dx, dy);
419
420 Creates a new hatched fill with the fg color used for the 1 bits in
421 the hatch and bg for the 0 bits.  If combine is non-zero alpha values
422 will be combined.
423
424 If cust_hatch is non-NULL it should be a pointer to 8 bytes of the
425 hash definition, with the high-bits to the left.
426
427 If cust_hatch is NULL then one of the standard hatches is used.
428
429 (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.
430
431 =cut
432 */
433 i_fill_t *
434 i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch, 
435             const unsigned char *cust_hatch, int dx, int dy) {
436   return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, 
437                          dx, dy);
438 }
439
440 /*
441 =item i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
442
443 =category Fills
444 =synopsis i_fill_t *fill = i_new_fill_hatchf(&fg_fcolor, &bg_fcolor, combine, hatch, custom_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(const i_fcolor *fg, const i_fcolor *bg, int combine, int hatch, 
461                   const 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 static struct i_fill_image_t
479 image_fill_proto =
480   {
481     {
482       fill_image,
483       fill_imagef,
484       NULL
485     }
486   };
487
488 /*
489 =item i_new_fill_image(im, matrix, xoff, yoff, combine)
490
491 =category Fills
492 =synopsis i_fill_t *fill = i_new_fill_image(src_img, matrix, x_offset, y_offset, combine);
493
494 Create an image based fill.
495
496 matrix is an array of 9 doubles representing a transformation matrix.
497
498 xoff and yoff are the offset into the image to start filling from.
499
500 =cut
501 */
502 i_fill_t *
503 i_new_fill_image(i_img *im, const double *matrix, int xoff, int yoff, int combine) {
504   struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */
505
506   *fill = image_fill_proto;
507
508   if (combine) {
509     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
510   }
511   else {
512     fill->base.combine = NULL;
513     fill->base.combinef = NULL;
514   }
515   fill->src = im;
516   if (xoff < 0)
517     xoff += im->xsize;
518   fill->xoff = xoff;
519   if (yoff < 0)
520     yoff += im->ysize;
521   fill->yoff = yoff;
522   if (matrix) {
523     fill->has_matrix = 1;
524     memcpy(fill->matrix, matrix, sizeof(fill->matrix));
525   }
526   else
527     fill->has_matrix = 0;
528
529   return &fill->base;
530 }
531
532 static void fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
533                        i_color *data);
534 static void fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
535                        i_fcolor *data);
536
537 struct i_fill_opacity_t {
538   i_fill_t base;
539   i_fill_t *other_fill;
540   double alpha_mult;
541 };
542
543 static struct i_fill_opacity_t
544 opacity_fill_proto =
545   {
546     fill_opacity,
547     fill_opacityf,
548     NULL
549   };
550
551 i_fill_t *
552 i_new_fill_opacity(i_fill_t *base_fill, double alpha_mult) {
553   struct i_fill_opacity_t *fill = mymalloc(sizeof(*fill));
554   *fill = opacity_fill_proto;
555
556   fill->base.combine = base_fill->combine;
557   fill->base.combinef = base_fill->combinef;
558
559   fill->other_fill = base_fill;
560   fill->alpha_mult = alpha_mult;
561
562   return &fill->base;
563 }
564
565 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
566
567 /*
568 =back
569
570 =head1 INTERNAL FUNCTIONS
571
572 =over
573
574 =item fill_solid(fill, x, y, width, channels, data)
575
576 The 8-bit sample fill function for non-combining solid fills.
577
578 =cut
579 */
580 static void
581 fill_solid(i_fill_t *fill, int x, int y, int width, int channels, 
582            i_color *data) {
583   i_color c = T_SOLID_FILL(fill)->c;
584   i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
585   while (width-- > 0) {
586     *data++ = c;
587   }
588 }
589
590 /*
591 =item fill_solid(fill, x, y, width, channels, data)
592
593 The floating sample fill function for non-combining solid fills.
594
595 =cut
596 */
597 static void
598 fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, 
599            i_fcolor *data) {
600   i_fcolor c = T_SOLID_FILL(fill)->fc;
601   i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
602   while (width-- > 0) {
603     *data++ = c;
604   }
605 }
606
607 static i_fill_hatch_t
608 hatch_fill_proto =
609   {
610     {
611       fill_hatch,
612       fill_hatchf,
613       NULL
614     }
615   };
616
617 /*
618 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
619
620 Implements creation of hatch fill objects.
621
622 =cut
623 */
624 static
625 i_fill_t *
626 i_new_hatch_low(const i_color *fg, const i_color *bg, 
627                 const i_fcolor *ffg, const i_fcolor *fbg, 
628                 int combine, int hatch, const unsigned char *cust_hatch,
629                 int dx, int dy) {
630   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
631
632   *fill = hatch_fill_proto;
633   /* Some Sun C didn't like the condition expressions that were here.
634      See https://rt.cpan.org/Ticket/Display.html?id=21944
635    */
636   if (fg)
637     fill->fg = *fg;
638   else
639     fill->fg = fcolor_to_color(ffg);
640   if (bg)
641     fill->bg = *bg;
642   else
643     fill->bg = fcolor_to_color(fbg);
644   if (ffg) 
645     fill->ffg = *ffg;
646   else
647     fill->ffg = color_to_fcolor(fg);
648   if (fbg)
649     fill->fbg = *fbg;
650   else
651     fill->fbg = color_to_fcolor(bg);
652   if (combine) {
653     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
654   }
655   else {
656     fill->base.combine = NULL;
657     fill->base.combinef = NULL;
658   }
659   if (cust_hatch) {
660     memcpy(fill->hatch, cust_hatch, 8);
661   }
662   else {
663     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
664       hatch = 0;
665     memcpy(fill->hatch, builtin_hatches[hatch], 8);
666   }
667   fill->dx = dx & 7;
668   fill->dy = dy & 7;
669
670   return &fill->base;
671 }
672
673 /*
674 =item fill_hatch(fill, x, y, width, channels, data)
675
676 The 8-bit sample fill function for hatched fills.
677
678 =cut
679 */
680 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
681                        i_color *data) {
682   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
683   int byte = f->hatch[(y + f->dy) & 7];
684   int xpos = (x + f->dx) & 7;
685   int mask = 128 >> xpos;
686   i_color fg = f->fg;
687   i_color bg = f->bg;
688   int want_channels = channels > 2 ? 4 : 2;
689
690   if (channels < 3) {
691     i_adapt_colors(2, 4, &fg, 1);
692     i_adapt_colors(2, 4, &bg, 1);
693   }
694
695   while (width-- > 0) {
696     if (byte & mask)
697       *data++ = fg;
698     else
699       *data++ = bg;
700     
701     if ((mask >>= 1) == 0)
702       mask = 128;
703   }
704 }
705
706 /*
707 =item fill_hatchf(fill, x, y, width, channels, data)
708
709 The floating sample fill function for hatched fills.
710
711 =back
712 */
713 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
714                         i_fcolor *data) {
715   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
716   int byte = f->hatch[(y + f->dy) & 7];
717   int xpos = (x + f->dx) & 7;
718   int mask = 128 >> xpos;
719   i_fcolor fg = f->ffg;
720   i_fcolor bg = f->fbg;
721
722   if (channels < 3) {
723     i_adapt_fcolors(2, 4, &fg, 1);
724     i_adapt_fcolors(2, 4, &bg, 1);
725   }
726   
727   while (width-- > 0) {
728     if (byte & mask)
729       *data++ = fg;
730     else
731       *data++ = bg;
732     
733     if ((mask >>= 1) == 0)
734       mask = 128;
735   }
736 }
737
738 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
739 /* linear interpolation */
740 static i_color interp_i_color(i_color before, i_color after, double pos,
741                               int channels) {
742   i_color out;
743   int ch;
744
745   pos -= floor(pos);
746   for (ch = 0; ch < channels; ++ch)
747     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
748   if (channels > 3 && out.channel[3])
749     for (ch = 0; ch < channels; ++ch)
750       if (ch != 3) {
751         int temp = out.channel[ch] * 255 / out.channel[3];
752         if (temp > 255)
753           temp = 255;
754         out.channel[ch] = temp;
755       }
756
757   return out;
758 }
759
760 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
761 /* linear interpolation */
762 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
763                                 int channels) {
764   i_fcolor out;
765   int ch;
766
767   pos -= floor(pos);
768   for (ch = 0; ch < channels; ++ch)
769     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
770   if (out.channel[3])
771     for (ch = 0; ch < channels; ++ch)
772       if (ch != 3) {
773         int temp = out.channel[ch] / out.channel[3];
774         if (temp > 1.0)
775           temp = 1.0;
776         out.channel[ch] = temp;
777       }
778
779   return out;
780 }
781
782 /*
783 =item fill_image(fill, x, y, width, channels, data, work)
784
785 =cut
786 */
787 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
788                        i_color *data) {
789   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
790   int i = 0;
791   i_color *out = data;
792   int want_channels = channels > 2 ? 4 : 2;
793   
794   if (f->has_matrix) {
795     /* the hard way */
796     while (i < width) {
797       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
798       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
799       double ix = floor(rx / f->src->xsize);
800       double iy = floor(ry / f->src->ysize);
801       i_color c[2][2];
802       i_color c2[2];
803       int dy;
804
805       if (f->xoff) {
806         rx += iy * f->xoff;
807         ix = floor(rx / f->src->xsize);
808       }
809       else if (f->yoff) {
810         ry += ix * f->yoff;
811         iy = floor(ry / f->src->ysize);
812       }
813       rx -= ix * f->src->xsize;
814       ry -= iy * f->src->ysize;
815
816       for (dy = 0; dy < 2; ++dy) {
817         if ((int)rx == f->src->xsize-1) {
818           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
819           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
820         }
821         else {
822           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
823                  c[dy]);
824         }
825         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
826       }
827       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
828       ++i;
829     }
830   }
831   else {
832     /* the easy way */
833     /* this should be possible to optimize to use i_glin() */
834     while (i < width) {
835       int rx = x+i;
836       int ry = y;
837       int ix = rx / f->src->xsize;
838       int iy = ry / f->src->ysize;
839
840       if (f->xoff) {
841         rx += iy * f->xoff;
842         ix = rx / f->src->xsize;
843       }
844       else if (f->yoff) {
845         ry += ix * f->yoff;
846         iy = ry / f->src->xsize;
847       }
848       rx -= ix * f->src->xsize;
849       ry -= iy * f->src->ysize;
850       i_gpix(f->src, rx, ry, out);
851       ++out;
852       ++i;
853     }
854   }
855   if (f->src->channels != want_channels)
856     i_adapt_colors(want_channels, f->src->channels, data, width);
857 }
858
859 /*
860 =item fill_imagef(fill, x, y, width, channels, data, work)
861
862 =cut
863 */
864 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
865                        i_fcolor *data) {
866   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
867   int i = 0;
868   int want_channels = channels > 2 ? 4 : 2;
869   
870   if (f->has_matrix) {
871     i_fcolor *work_data = data;
872     /* the hard way */
873     while (i < width) {
874       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
875       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
876       double ix = floor(rx / f->src->xsize);
877       double iy = floor(ry / f->src->ysize);
878       i_fcolor c[2][2];
879       i_fcolor c2[2];
880       int dy;
881
882       if (f->xoff) {
883         rx += iy * f->xoff;
884         ix = floor(rx / f->src->xsize);
885       }
886       else if (f->yoff) {
887         ry += ix * f->yoff;
888         iy = floor(ry / f->src->ysize);
889       }
890       rx -= ix * f->src->xsize;
891       ry -= iy * f->src->ysize;
892
893       for (dy = 0; dy < 2; ++dy) {
894         if ((int)rx == f->src->xsize-1) {
895           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
896           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
897         }
898         else {
899           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
900                  c[dy]);
901         }
902         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
903       }
904       *work_data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
905       ++i;
906     }
907   }
908   else {
909     i_fcolor *work_data = data;
910     /* the easy way */
911     /* this should be possible to optimize to use i_glin() */
912     while (i < width) {
913       int rx = x+i;
914       int ry = y;
915       int ix = rx / f->src->xsize;
916       int iy = ry / f->src->ysize;
917
918       if (f->xoff) {
919         rx += iy * f->xoff;
920         ix = rx / f->src->xsize;
921       }
922       else if (f->yoff) {
923         ry += ix * f->yoff;
924         iy = ry / f->src->xsize;
925       }
926       rx -= ix * f->src->xsize;
927       ry -= iy * f->src->ysize;
928       i_gpixf(f->src, rx, ry, work_data);
929       ++work_data;
930       ++i;
931     }
932   }
933   if (f->src->channels != want_channels)
934     i_adapt_fcolors(want_channels, f->src->channels, data, width);
935 }
936
937 static void 
938 fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
939              i_color *data) {
940   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
941   int alpha_chan = channels-1; /* channels is always 2 or 4 */
942   i_color *datap = data;
943   
944   (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data);
945   while (width--) {
946     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
947     if (new_alpha < 0) 
948       datap->channel[alpha_chan] = 0;
949     else if (new_alpha > 255)
950       datap->channel[alpha_chan] = 255;
951     else datap->channel[alpha_chan] = (int)(new_alpha + 0.5);
952
953     ++datap;
954   }
955 }
956 static void 
957 fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
958             i_fcolor *data) {
959   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
960   int alpha_chan = channels-1; /* channels is always 2 or 4 */
961   i_fcolor *datap = data;
962   
963   (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data);
964   
965   while (width--) {
966     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
967     if (new_alpha < 0) 
968       datap->channel[alpha_chan] = 0;
969     else if (new_alpha > 1.0)
970       datap->channel[alpha_chan] = 1.0;
971     else datap->channel[alpha_chan] = new_alpha;
972
973     ++datap;
974   }
975 }
976
977 /*
978 =back
979
980 =head1 AUTHOR
981
982 Tony Cook <tony@develop-help.com>
983
984 =head1 SEE ALSO
985
986 Imager(3)
987
988 =cut
989 */