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