]> git.imager.perl.org - imager.git/blob - fills.c
check for the uninitialized gif89 bug in 4.2.0 and probe for the 5.0.1 api
[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 *, i_img_dim x, i_img_dim y, i_img_dim width,
167                        int channels, i_color *);
168 static void fill_solidf(i_fill_t *, i_img_dim x, i_img_dim y, i_img_dim width,
169                         int channels, 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   i_img_dim dx, dy;
402 } i_fill_hatch_t;
403
404 static void fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y,
405                        i_img_dim width, int channels, i_color *data);
406 static void fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y,
407                         i_img_dim width, int channels, 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                 i_img_dim dx, i_img_dim dy);
413
414 /*
415 =item i_new_fill_hatch(C<fg>, C<bg>, C<combine>, C<hatch>, C<cust_hatch>, C<dx>, C<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 C<fg> color used for the 1 bits in
421 the hatch and C<bg> for the 0 bits.  If C<combine> is non-zero alpha
422 values will be combined.
423
424 If C<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 C<cust_hatch> is NULL then one of the standard hatches is used.
428
429 (C<dx>, C<dy>) are an offset into the hatch which can be used to hatch
430 adjoining areas out of alignment, or to align the origin of a hatch
431 with the the side of a filled area.
432
433 =cut
434 */
435 i_fill_t *
436 i_new_fill_hatch(const i_color *fg, const i_color *bg, int combine, int hatch, 
437             const unsigned char *cust_hatch, i_img_dim dx, i_img_dim dy) {
438   return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch, 
439                          dx, dy);
440 }
441
442 /*
443 =item i_new_fill_hatchf(C<fg>, C<bg>, C<combine>, C<hatch>, C<cust_hatch>, C<dx>, C<dy>)
444
445 =category Fills
446 =synopsis i_fill_t *fill = i_new_fill_hatchf(&fg_fcolor, &bg_fcolor, combine, hatch, custom_hatch, dx, dy);
447
448 Creates a new hatched fill with the C<fg> color used for the 1 bits in
449 the hatch and C<bg> for the 0 bits.  If C<combine> is non-zero alpha
450 values will be combined.
451
452 If C<cust_hatch> is non-NULL it should be a pointer to 8 bytes of the
453 hash definition, with the high-bits to the left.
454
455 If C<cust_hatch> is NULL then one of the standard hatches is used.
456
457 (C<dx>, C<dy>) are an offset into the hatch which can be used to hatch
458 adjoining areas out of alignment, or to align the origin of a hatch
459 with the the side of a filled area.
460
461 =cut
462 */
463 i_fill_t *
464 i_new_fill_hatchf(const i_fcolor *fg, const i_fcolor *bg, int combine, int hatch, 
465                   const unsigned char *cust_hatch, i_img_dim dx, i_img_dim dy) {
466   return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch, 
467                          dx, dy);
468 }
469
470 static void fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y,
471                        i_img_dim width, int channels, i_color *data);
472 static void fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y,
473                         i_img_dim width, int channels, i_fcolor *data);
474 struct i_fill_image_t {
475   i_fill_t base;
476   i_img *src;
477   i_img_dim xoff, yoff;
478   int has_matrix;
479   double matrix[9];
480 };
481
482 static struct i_fill_image_t
483 image_fill_proto =
484   {
485     {
486       fill_image,
487       fill_imagef,
488       NULL
489     }
490   };
491
492 /*
493 =item i_new_fill_image(C<im>, C<matrix>, C<xoff>, C<yoff>, C<combine>)
494
495 =category Fills
496 =synopsis i_fill_t *fill = i_new_fill_image(src_img, matrix, x_offset, y_offset, combine);
497
498 Create an image based fill.
499
500 matrix is an array of 9 doubles representing a transformation matrix.
501
502 C<xoff> and C<yoff> are the offset into the image to start filling from.
503
504 =cut
505 */
506 i_fill_t *
507 i_new_fill_image(i_img *im, const double *matrix, i_img_dim xoff, i_img_dim yoff, int combine) {
508   struct i_fill_image_t *fill = mymalloc(sizeof(*fill)); /* checked 14jul05 tonyc */
509
510   *fill = image_fill_proto;
511
512   if (combine) {
513     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
514   }
515   else {
516     fill->base.combine = NULL;
517     fill->base.combinef = NULL;
518   }
519   fill->src = im;
520   if (xoff < 0)
521     xoff += im->xsize;
522   fill->xoff = xoff;
523   if (yoff < 0)
524     yoff += im->ysize;
525   fill->yoff = yoff;
526   if (matrix) {
527     fill->has_matrix = 1;
528     memcpy(fill->matrix, matrix, sizeof(fill->matrix));
529   }
530   else
531     fill->has_matrix = 0;
532
533   return &fill->base;
534 }
535
536 static void fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y,
537                          i_img_dim width, int channels, i_color *data);
538 static void fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y,
539                           i_img_dim width, int channels, i_fcolor *data);
540
541 struct i_fill_opacity_t {
542   i_fill_t base;
543   i_fill_t *other_fill;
544   double alpha_mult;
545 };
546
547 static struct i_fill_opacity_t
548 opacity_fill_proto =
549   {
550     {
551       fill_opacity,
552       fill_opacityf,
553       NULL
554     }
555   };
556
557 i_fill_t *
558 i_new_fill_opacity(i_fill_t *base_fill, double alpha_mult) {
559   struct i_fill_opacity_t *fill = mymalloc(sizeof(*fill));
560   *fill = opacity_fill_proto;
561
562   fill->base.combine = base_fill->combine;
563   fill->base.combinef = base_fill->combinef;
564
565   fill->other_fill = base_fill;
566   fill->alpha_mult = alpha_mult;
567
568   if (!base_fill->f_fill_with_color) {
569     /* base fill only does floating, so we only do that too */
570     fill->base.f_fill_with_color = NULL;
571   }
572
573   return &fill->base;
574 }
575
576 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
577
578 /*
579 =back
580
581 =head1 INTERNAL FUNCTIONS
582
583 =over
584
585 =item fill_solid(fill, x, y, width, channels, data)
586
587 The 8-bit sample fill function for non-combining solid fills.
588
589 =cut
590 */
591 static void
592 fill_solid(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
593            int channels, i_color *data) {
594   i_color c = T_SOLID_FILL(fill)->c;
595   i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
596   while (width-- > 0) {
597     *data++ = c;
598   }
599 }
600
601 /*
602 =item fill_solid(fill, x, y, width, channels, data)
603
604 The floating sample fill function for non-combining solid fills.
605
606 =cut
607 */
608 static void
609 fill_solidf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
610             int channels, i_fcolor *data) {
611   i_fcolor c = T_SOLID_FILL(fill)->fc;
612   i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
613   while (width-- > 0) {
614     *data++ = c;
615   }
616 }
617
618 static i_fill_hatch_t
619 hatch_fill_proto =
620   {
621     {
622       fill_hatch,
623       fill_hatchf,
624       NULL
625     }
626   };
627
628 /*
629 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
630
631 Implements creation of hatch fill objects.
632
633 =cut
634 */
635 static
636 i_fill_t *
637 i_new_hatch_low(const i_color *fg, const i_color *bg, 
638                 const i_fcolor *ffg, const i_fcolor *fbg, 
639                 int combine, int hatch, const unsigned char *cust_hatch,
640                 i_img_dim dx, i_img_dim dy) {
641   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
642
643   *fill = hatch_fill_proto;
644   /* Some Sun C didn't like the condition expressions that were here.
645      See https://rt.cpan.org/Ticket/Display.html?id=21944
646    */
647   if (fg)
648     fill->fg = *fg;
649   else
650     fill->fg = fcolor_to_color(ffg);
651   if (bg)
652     fill->bg = *bg;
653   else
654     fill->bg = fcolor_to_color(fbg);
655   if (ffg) 
656     fill->ffg = *ffg;
657   else
658     fill->ffg = color_to_fcolor(fg);
659   if (fbg)
660     fill->fbg = *fbg;
661   else
662     fill->fbg = color_to_fcolor(bg);
663   if (combine) {
664     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
665   }
666   else {
667     fill->base.combine = NULL;
668     fill->base.combinef = NULL;
669   }
670   if (cust_hatch) {
671     memcpy(fill->hatch, cust_hatch, 8);
672   }
673   else {
674     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
675       hatch = 0;
676     memcpy(fill->hatch, builtin_hatches[hatch], 8);
677   }
678   fill->dx = dx & 7;
679   fill->dy = dy & 7;
680
681   return &fill->base;
682 }
683
684 /*
685 =item fill_hatch(fill, x, y, width, channels, data)
686
687 The 8-bit sample fill function for hatched fills.
688
689 =cut
690 */
691 static void 
692 fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
693            int channels, i_color *data) {
694   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
695   int byte = f->hatch[(y + f->dy) & 7];
696   int xpos = (x + f->dx) & 7;
697   int mask = 128 >> xpos;
698   i_color fg = f->fg;
699   i_color bg = f->bg;
700
701   if (channels < 3) {
702     i_adapt_colors(2, 4, &fg, 1);
703     i_adapt_colors(2, 4, &bg, 1);
704   }
705
706   while (width-- > 0) {
707     if (byte & mask)
708       *data++ = fg;
709     else
710       *data++ = bg;
711     
712     if ((mask >>= 1) == 0)
713       mask = 128;
714   }
715 }
716
717 /*
718 =item fill_hatchf(fill, x, y, width, channels, data)
719
720 The floating sample fill function for hatched fills.
721
722 =back
723 */
724 static void
725 fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
726             int channels, i_fcolor *data) {
727   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
728   int byte = f->hatch[(y + f->dy) & 7];
729   int xpos = (x + f->dx) & 7;
730   int mask = 128 >> xpos;
731   i_fcolor fg = f->ffg;
732   i_fcolor bg = f->fbg;
733
734   if (channels < 3) {
735     i_adapt_fcolors(2, 4, &fg, 1);
736     i_adapt_fcolors(2, 4, &bg, 1);
737   }
738   
739   while (width-- > 0) {
740     if (byte & mask)
741       *data++ = fg;
742     else
743       *data++ = bg;
744     
745     if ((mask >>= 1) == 0)
746       mask = 128;
747   }
748 }
749
750 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
751 /* linear interpolation */
752 static i_color interp_i_color(i_color before, i_color after, double pos,
753                               int channels) {
754   i_color out;
755   int ch;
756
757   pos -= floor(pos);
758   for (ch = 0; ch < channels; ++ch)
759     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
760   if (channels > 3 && out.channel[3])
761     for (ch = 0; ch < channels; ++ch)
762       if (ch != 3) {
763         int temp = out.channel[ch] * 255 / out.channel[3];
764         if (temp > 255)
765           temp = 255;
766         out.channel[ch] = temp;
767       }
768
769   return out;
770 }
771
772 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
773 /* linear interpolation */
774 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
775                                 int channels) {
776   i_fcolor out;
777   int ch;
778
779   pos -= floor(pos);
780   for (ch = 0; ch < channels; ++ch)
781     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
782   if (out.channel[3])
783     for (ch = 0; ch < channels; ++ch)
784       if (ch != 3) {
785         int temp = out.channel[ch] / out.channel[3];
786         if (temp > 1.0)
787           temp = 1.0;
788         out.channel[ch] = temp;
789       }
790
791   return out;
792 }
793
794 /*
795 =item fill_image(fill, x, y, width, channels, data, work)
796
797 =cut
798 */
799 static void
800 fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
801            int channels, i_color *data) {
802   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
803   i_img_dim i = 0;
804   i_color *out = data;
805   int want_channels = channels > 2 ? 4 : 2;
806   
807   if (f->has_matrix) {
808     /* the hard way */
809     while (i < width) {
810       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
811       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
812       double ix = floor(rx / f->src->xsize);
813       double iy = floor(ry / f->src->ysize);
814       i_color c[2][2];
815       i_color c2[2];
816       i_img_dim dy;
817
818       if (f->xoff) {
819         rx += iy * f->xoff;
820         ix = floor(rx / f->src->xsize);
821       }
822       else if (f->yoff) {
823         ry += ix * f->yoff;
824         iy = floor(ry / f->src->ysize);
825       }
826       rx -= ix * f->src->xsize;
827       ry -= iy * f->src->ysize;
828
829       for (dy = 0; dy < 2; ++dy) {
830         if ((i_img_dim)rx == f->src->xsize-1) {
831           i_gpix(f->src, f->src->xsize-1, ((i_img_dim)ry+dy) % f->src->ysize, &c[dy][0]);
832           i_gpix(f->src, 0, ((i_img_dim)ry+dy) % f->src->xsize, &c[dy][1]);
833         }
834         else {
835           i_glin(f->src, (i_img_dim)rx, (i_img_dim)rx+2, ((i_img_dim)ry+dy) % f->src->ysize, 
836                  c[dy]);
837         }
838         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
839       }
840       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
841       ++i;
842     }
843   }
844   else {
845     /* the easy way */
846     /* this should be possible to optimize to use i_glin() */
847     while (i < width) {
848       i_img_dim rx = x+i;
849       i_img_dim ry = y;
850       i_img_dim ix = rx / f->src->xsize;
851       i_img_dim iy = ry / f->src->ysize;
852
853       if (f->xoff) {
854         rx += iy * f->xoff;
855         ix = rx / f->src->xsize;
856       }
857       else if (f->yoff) {
858         ry += ix * f->yoff;
859         iy = ry / f->src->xsize;
860       }
861       rx -= ix * f->src->xsize;
862       ry -= iy * f->src->ysize;
863       i_gpix(f->src, rx, ry, out);
864       ++out;
865       ++i;
866     }
867   }
868   if (f->src->channels != want_channels)
869     i_adapt_colors(want_channels, f->src->channels, data, width);
870 }
871
872 /*
873 =item fill_imagef(fill, x, y, width, channels, data, work)
874
875 =cut
876 */
877 static void
878 fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
879             int channels, i_fcolor *data) {
880   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
881   i_img_dim i = 0;
882   int want_channels = channels > 2 ? 4 : 2;
883   
884   if (f->has_matrix) {
885     i_fcolor *work_data = data;
886     /* the hard way */
887     while (i < width) {
888       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
889       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
890       double ix = floor(rx / f->src->xsize);
891       double iy = floor(ry / f->src->ysize);
892       i_fcolor c[2][2];
893       i_fcolor c2[2];
894       i_img_dim dy;
895
896       if (f->xoff) {
897         rx += iy * f->xoff;
898         ix = floor(rx / f->src->xsize);
899       }
900       else if (f->yoff) {
901         ry += ix * f->yoff;
902         iy = floor(ry / f->src->ysize);
903       }
904       rx -= ix * f->src->xsize;
905       ry -= iy * f->src->ysize;
906
907       for (dy = 0; dy < 2; ++dy) {
908         if ((i_img_dim)rx == f->src->xsize-1) {
909           i_gpixf(f->src, f->src->xsize-1, ((i_img_dim)ry+dy) % f->src->ysize, &c[dy][0]);
910           i_gpixf(f->src, 0, ((i_img_dim)ry+dy) % f->src->xsize, &c[dy][1]);
911         }
912         else {
913           i_glinf(f->src, (i_img_dim)rx, (i_img_dim)rx+2, ((i_img_dim)ry+dy) % f->src->ysize, 
914                  c[dy]);
915         }
916         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
917       }
918       *work_data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
919       ++i;
920     }
921   }
922   else {
923     i_fcolor *work_data = data;
924     /* the easy way */
925     /* this should be possible to optimize to use i_glin() */
926     while (i < width) {
927       i_img_dim rx = x+i;
928       i_img_dim ry = y;
929       i_img_dim ix = rx / f->src->xsize;
930       i_img_dim iy = ry / f->src->ysize;
931
932       if (f->xoff) {
933         rx += iy * f->xoff;
934         ix = rx / f->src->xsize;
935       }
936       else if (f->yoff) {
937         ry += ix * f->yoff;
938         iy = ry / f->src->xsize;
939       }
940       rx -= ix * f->src->xsize;
941       ry -= iy * f->src->ysize;
942       i_gpixf(f->src, rx, ry, work_data);
943       ++work_data;
944       ++i;
945     }
946   }
947   if (f->src->channels != want_channels)
948     i_adapt_fcolors(want_channels, f->src->channels, data, width);
949 }
950
951 static void 
952 fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
953              int channels, i_color *data) {
954   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
955   int alpha_chan = channels > 2 ? 3 : 1;
956   i_color *datap = data;
957   
958   (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data);
959   while (width--) {
960     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
961     if (new_alpha < 0) 
962       datap->channel[alpha_chan] = 0;
963     else if (new_alpha > 255)
964       datap->channel[alpha_chan] = 255;
965     else datap->channel[alpha_chan] = (int)(new_alpha + 0.5);
966
967     ++datap;
968   }
969 }
970 static void 
971 fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
972               int channels, i_fcolor *data) {
973   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
974   int alpha_chan = channels > 2 ? 3 : 1;
975   i_fcolor *datap = data;
976   
977   (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data);
978   
979   while (width--) {
980     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
981     if (new_alpha < 0) 
982       datap->channel[alpha_chan] = 0;
983     else if (new_alpha > 1.0)
984       datap->channel[alpha_chan] = 1.0;
985     else datap->channel[alpha_chan] = new_alpha;
986
987     ++datap;
988   }
989 }
990
991 /*
992 =back
993
994 =head1 AUTHOR
995
996 Tony Cook <tony@develop-help.com>
997
998 =head1 SEE ALSO
999
1000 Imager(3)
1001
1002 =cut
1003 */