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