]> git.imager.perl.org - imager.git/blob - fills.c
don't index W32
[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(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, int dx, int 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, int dx, int 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, int x, int y, int width, int channels,
471                        i_color *data);
472 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
473                        i_fcolor *data);
474 struct i_fill_image_t {
475   i_fill_t base;
476   i_img *src;
477   int 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, int xoff, int 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, int x, int y, int width, int channels,
537                        i_color *data);
538 static void fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
539                        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     fill_opacity,
551     fill_opacityf,
552     NULL
553   };
554
555 i_fill_t *
556 i_new_fill_opacity(i_fill_t *base_fill, double alpha_mult) {
557   struct i_fill_opacity_t *fill = mymalloc(sizeof(*fill));
558   *fill = opacity_fill_proto;
559
560   fill->base.combine = base_fill->combine;
561   fill->base.combinef = base_fill->combinef;
562
563   fill->other_fill = base_fill;
564   fill->alpha_mult = alpha_mult;
565
566   if (!base_fill->f_fill_with_color) {
567     /* base fill only does floating, so we only do that too */
568     fill->base.f_fill_with_color = NULL;
569   }
570
571   return &fill->base;
572 }
573
574 #define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
575
576 /*
577 =back
578
579 =head1 INTERNAL FUNCTIONS
580
581 =over
582
583 =item fill_solid(fill, x, y, width, channels, data)
584
585 The 8-bit sample fill function for non-combining solid fills.
586
587 =cut
588 */
589 static void
590 fill_solid(i_fill_t *fill, int x, int y, int width, int channels, 
591            i_color *data) {
592   i_color c = T_SOLID_FILL(fill)->c;
593   i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
594   while (width-- > 0) {
595     *data++ = c;
596   }
597 }
598
599 /*
600 =item fill_solid(fill, x, y, width, channels, data)
601
602 The floating sample fill function for non-combining solid fills.
603
604 =cut
605 */
606 static void
607 fill_solidf(i_fill_t *fill, int x, int y, int width, int channels, 
608            i_fcolor *data) {
609   i_fcolor c = T_SOLID_FILL(fill)->fc;
610   i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
611   while (width-- > 0) {
612     *data++ = c;
613   }
614 }
615
616 static i_fill_hatch_t
617 hatch_fill_proto =
618   {
619     {
620       fill_hatch,
621       fill_hatchf,
622       NULL
623     }
624   };
625
626 /*
627 =item i_new_hatch_low(fg, bg, ffg, fbg, combine, hatch, cust_hatch, dx, dy)
628
629 Implements creation of hatch fill objects.
630
631 =cut
632 */
633 static
634 i_fill_t *
635 i_new_hatch_low(const i_color *fg, const i_color *bg, 
636                 const i_fcolor *ffg, const i_fcolor *fbg, 
637                 int combine, int hatch, const unsigned char *cust_hatch,
638                 int dx, int dy) {
639   i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t)); /* checked 14jul05 tonyc */
640
641   *fill = hatch_fill_proto;
642   /* Some Sun C didn't like the condition expressions that were here.
643      See https://rt.cpan.org/Ticket/Display.html?id=21944
644    */
645   if (fg)
646     fill->fg = *fg;
647   else
648     fill->fg = fcolor_to_color(ffg);
649   if (bg)
650     fill->bg = *bg;
651   else
652     fill->bg = fcolor_to_color(fbg);
653   if (ffg) 
654     fill->ffg = *ffg;
655   else
656     fill->ffg = color_to_fcolor(fg);
657   if (fbg)
658     fill->fbg = *fbg;
659   else
660     fill->fbg = color_to_fcolor(bg);
661   if (combine) {
662     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
663   }
664   else {
665     fill->base.combine = NULL;
666     fill->base.combinef = NULL;
667   }
668   if (cust_hatch) {
669     memcpy(fill->hatch, cust_hatch, 8);
670   }
671   else {
672     if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches)) 
673       hatch = 0;
674     memcpy(fill->hatch, builtin_hatches[hatch], 8);
675   }
676   fill->dx = dx & 7;
677   fill->dy = dy & 7;
678
679   return &fill->base;
680 }
681
682 /*
683 =item fill_hatch(fill, x, y, width, channels, data)
684
685 The 8-bit sample fill function for hatched fills.
686
687 =cut
688 */
689 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
690                        i_color *data) {
691   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
692   int byte = f->hatch[(y + f->dy) & 7];
693   int xpos = (x + f->dx) & 7;
694   int mask = 128 >> xpos;
695   i_color fg = f->fg;
696   i_color bg = f->bg;
697   int want_channels = channels > 2 ? 4 : 2;
698
699   if (channels < 3) {
700     i_adapt_colors(2, 4, &fg, 1);
701     i_adapt_colors(2, 4, &bg, 1);
702   }
703
704   while (width-- > 0) {
705     if (byte & mask)
706       *data++ = fg;
707     else
708       *data++ = bg;
709     
710     if ((mask >>= 1) == 0)
711       mask = 128;
712   }
713 }
714
715 /*
716 =item fill_hatchf(fill, x, y, width, channels, data)
717
718 The floating sample fill function for hatched fills.
719
720 =back
721 */
722 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
723                         i_fcolor *data) {
724   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
725   int byte = f->hatch[(y + f->dy) & 7];
726   int xpos = (x + f->dx) & 7;
727   int mask = 128 >> xpos;
728   i_fcolor fg = f->ffg;
729   i_fcolor bg = f->fbg;
730
731   if (channels < 3) {
732     i_adapt_fcolors(2, 4, &fg, 1);
733     i_adapt_fcolors(2, 4, &bg, 1);
734   }
735   
736   while (width-- > 0) {
737     if (byte & mask)
738       *data++ = fg;
739     else
740       *data++ = bg;
741     
742     if ((mask >>= 1) == 0)
743       mask = 128;
744   }
745 }
746
747 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
748 /* linear interpolation */
749 static i_color interp_i_color(i_color before, i_color after, double pos,
750                               int channels) {
751   i_color out;
752   int ch;
753
754   pos -= floor(pos);
755   for (ch = 0; ch < channels; ++ch)
756     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
757   if (channels > 3 && out.channel[3])
758     for (ch = 0; ch < channels; ++ch)
759       if (ch != 3) {
760         int temp = out.channel[ch] * 255 / out.channel[3];
761         if (temp > 255)
762           temp = 255;
763         out.channel[ch] = temp;
764       }
765
766   return out;
767 }
768
769 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
770 /* linear interpolation */
771 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
772                                 int channels) {
773   i_fcolor out;
774   int ch;
775
776   pos -= floor(pos);
777   for (ch = 0; ch < channels; ++ch)
778     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
779   if (out.channel[3])
780     for (ch = 0; ch < channels; ++ch)
781       if (ch != 3) {
782         int temp = out.channel[ch] / out.channel[3];
783         if (temp > 1.0)
784           temp = 1.0;
785         out.channel[ch] = temp;
786       }
787
788   return out;
789 }
790
791 /*
792 =item fill_image(fill, x, y, width, channels, data, work)
793
794 =cut
795 */
796 static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
797                        i_color *data) {
798   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
799   int i = 0;
800   i_color *out = data;
801   int want_channels = channels > 2 ? 4 : 2;
802   
803   if (f->has_matrix) {
804     /* the hard way */
805     while (i < width) {
806       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
807       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
808       double ix = floor(rx / f->src->xsize);
809       double iy = floor(ry / f->src->ysize);
810       i_color c[2][2];
811       i_color c2[2];
812       int dy;
813
814       if (f->xoff) {
815         rx += iy * f->xoff;
816         ix = floor(rx / f->src->xsize);
817       }
818       else if (f->yoff) {
819         ry += ix * f->yoff;
820         iy = floor(ry / f->src->ysize);
821       }
822       rx -= ix * f->src->xsize;
823       ry -= iy * f->src->ysize;
824
825       for (dy = 0; dy < 2; ++dy) {
826         if ((int)rx == f->src->xsize-1) {
827           i_gpix(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
828           i_gpix(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
829         }
830         else {
831           i_glin(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
832                  c[dy]);
833         }
834         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
835       }
836       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
837       ++i;
838     }
839   }
840   else {
841     /* the easy way */
842     /* this should be possible to optimize to use i_glin() */
843     while (i < width) {
844       int rx = x+i;
845       int ry = y;
846       int ix = rx / f->src->xsize;
847       int iy = ry / f->src->ysize;
848
849       if (f->xoff) {
850         rx += iy * f->xoff;
851         ix = rx / f->src->xsize;
852       }
853       else if (f->yoff) {
854         ry += ix * f->yoff;
855         iy = ry / f->src->xsize;
856       }
857       rx -= ix * f->src->xsize;
858       ry -= iy * f->src->ysize;
859       i_gpix(f->src, rx, ry, out);
860       ++out;
861       ++i;
862     }
863   }
864   if (f->src->channels != want_channels)
865     i_adapt_colors(want_channels, f->src->channels, data, width);
866 }
867
868 /*
869 =item fill_imagef(fill, x, y, width, channels, data, work)
870
871 =cut
872 */
873 static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
874                        i_fcolor *data) {
875   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
876   int i = 0;
877   int want_channels = channels > 2 ? 4 : 2;
878   
879   if (f->has_matrix) {
880     i_fcolor *work_data = data;
881     /* the hard way */
882     while (i < width) {
883       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
884       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
885       double ix = floor(rx / f->src->xsize);
886       double iy = floor(ry / f->src->ysize);
887       i_fcolor c[2][2];
888       i_fcolor c2[2];
889       int dy;
890
891       if (f->xoff) {
892         rx += iy * f->xoff;
893         ix = floor(rx / f->src->xsize);
894       }
895       else if (f->yoff) {
896         ry += ix * f->yoff;
897         iy = floor(ry / f->src->ysize);
898       }
899       rx -= ix * f->src->xsize;
900       ry -= iy * f->src->ysize;
901
902       for (dy = 0; dy < 2; ++dy) {
903         if ((int)rx == f->src->xsize-1) {
904           i_gpixf(f->src, f->src->xsize-1, ((int)ry+dy) % f->src->ysize, &c[dy][0]);
905           i_gpixf(f->src, 0, ((int)ry+dy) % f->src->xsize, &c[dy][1]);
906         }
907         else {
908           i_glinf(f->src, (int)rx, (int)rx+2, ((int)ry+dy) % f->src->ysize, 
909                  c[dy]);
910         }
911         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
912       }
913       *work_data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
914       ++i;
915     }
916   }
917   else {
918     i_fcolor *work_data = data;
919     /* the easy way */
920     /* this should be possible to optimize to use i_glin() */
921     while (i < width) {
922       int rx = x+i;
923       int ry = y;
924       int ix = rx / f->src->xsize;
925       int iy = ry / f->src->ysize;
926
927       if (f->xoff) {
928         rx += iy * f->xoff;
929         ix = rx / f->src->xsize;
930       }
931       else if (f->yoff) {
932         ry += ix * f->yoff;
933         iy = ry / f->src->xsize;
934       }
935       rx -= ix * f->src->xsize;
936       ry -= iy * f->src->ysize;
937       i_gpixf(f->src, rx, ry, work_data);
938       ++work_data;
939       ++i;
940     }
941   }
942   if (f->src->channels != want_channels)
943     i_adapt_fcolors(want_channels, f->src->channels, data, width);
944 }
945
946 static void 
947 fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
948              i_color *data) {
949   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
950   int alpha_chan = channels > 2 ? 3 : 1;
951   i_color *datap = data;
952   
953   (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data);
954   while (width--) {
955     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
956     if (new_alpha < 0) 
957       datap->channel[alpha_chan] = 0;
958     else if (new_alpha > 255)
959       datap->channel[alpha_chan] = 255;
960     else datap->channel[alpha_chan] = (int)(new_alpha + 0.5);
961
962     ++datap;
963   }
964 }
965 static void 
966 fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
967             i_fcolor *data) {
968   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
969   int alpha_chan = channels > 2 ? 3 : 1;
970   i_fcolor *datap = data;
971   
972   (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data);
973   
974   while (width--) {
975     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
976     if (new_alpha < 0) 
977       datap->channel[alpha_chan] = 0;
978     else if (new_alpha > 1.0)
979       datap->channel[alpha_chan] = 1.0;
980     else datap->channel[alpha_chan] = new_alpha;
981
982     ++datap;
983   }
984 }
985
986 /*
987 =back
988
989 =head1 AUTHOR
990
991 Tony Cook <tony@develop-help.com>
992
993 =head1 SEE ALSO
994
995 Imager(3)
996
997 =cut
998 */