add assertions for callers to i_new_hatch_low() in fills.c
[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   /* Some Sun C didn't like the condition expressions that were here.
651      See https://rt.cpan.org/Ticket/Display.html?id=21944
652    */
653   if (fg)
654     fill->fg = *fg;
655   else
656     fill->fg = fcolor_to_color(ffg);
657   if (bg)
658     fill->bg = *bg;
659   else
660     fill->bg = fcolor_to_color(fbg);
661   if (ffg) 
662     fill->ffg = *ffg;
663   else
664     fill->ffg = color_to_fcolor(fg);
665   if (fbg)
666     fill->fbg = *fbg;
667   else
668     fill->fbg = color_to_fcolor(bg);
669   if (combine) {
670     i_get_combine(combine, &fill->base.combine, &fill->base.combinef);
671   }
672   else {
673     fill->base.combine = NULL;
674     fill->base.combinef = NULL;
675   }
676   if (cust_hatch) {
677     memcpy(fill->hatch, cust_hatch, 8);
678   }
679   else {
680     if (hatch >= sizeof(builtin_hatches)/sizeof(*builtin_hatches)
681         || hatch < 0) {
682       hatch = 0;
683     }
684     memcpy(fill->hatch, builtin_hatches[hatch], 8);
685   }
686   fill->dx = dx & 7;
687   fill->dy = dy & 7;
688
689   return &fill->base;
690 }
691
692 /*
693 =item fill_hatch(fill, x, y, width, channels, data)
694
695 The 8-bit sample fill function for hatched fills.
696
697 =cut
698 */
699 static void 
700 fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
701            int channels, i_color *data) {
702   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
703   int byte = f->hatch[(y + f->dy) & 7];
704   int xpos = (x + f->dx) & 7;
705   int mask = 128 >> xpos;
706   i_color fg = f->fg;
707   i_color bg = f->bg;
708
709   if (channels < 3) {
710     i_adapt_colors(2, 4, &fg, 1);
711     i_adapt_colors(2, 4, &bg, 1);
712   }
713
714   while (width-- > 0) {
715     if (byte & mask)
716       *data++ = fg;
717     else
718       *data++ = bg;
719     
720     if ((mask >>= 1) == 0)
721       mask = 128;
722   }
723 }
724
725 /*
726 =item fill_hatchf(fill, x, y, width, channels, data)
727
728 The floating sample fill function for hatched fills.
729
730 =cut
731 */
732 static void
733 fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
734             int channels, i_fcolor *data) {
735   i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
736   int byte = f->hatch[(y + f->dy) & 7];
737   int xpos = (x + f->dx) & 7;
738   int mask = 128 >> xpos;
739   i_fcolor fg = f->ffg;
740   i_fcolor bg = f->fbg;
741
742   if (channels < 3) {
743     i_adapt_fcolors(2, 4, &fg, 1);
744     i_adapt_fcolors(2, 4, &bg, 1);
745   }
746   
747   while (width-- > 0) {
748     if (byte & mask)
749       *data++ = fg;
750     else
751       *data++ = bg;
752     
753     if ((mask >>= 1) == 0)
754       mask = 128;
755   }
756 }
757
758 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
759 /* linear interpolation */
760 static i_color interp_i_color(i_color before, i_color after, double pos,
761                               int channels) {
762   i_color out;
763   int ch;
764
765   pos -= floor(pos);
766   for (ch = 0; ch < channels; ++ch)
767     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
768   if (channels > 3 && out.channel[3]) {
769     for (ch = 0; ch < channels; ++ch) {
770       if (ch != 3) {
771         int temp = out.channel[ch] * 255 / out.channel[3];
772         if (temp > 255)
773           temp = 255;
774         out.channel[ch] = temp;
775       }
776     }
777   }
778
779   return out;
780 }
781
782 /* hopefully this will be inlined  (it is with -O3 with gcc 2.95.4) */
783 /* linear interpolation */
784 static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
785                                 int channels) {
786   i_fcolor out;
787   int ch;
788
789   pos -= floor(pos);
790   for (ch = 0; ch < channels; ++ch)
791     out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
792   if (out.channel[3]) {
793     for (ch = 0; ch < channels; ++ch) {
794       if (ch != 3) {
795         int temp = out.channel[ch] / out.channel[3];
796         if (temp > 1.0)
797           temp = 1.0;
798         out.channel[ch] = temp;
799       }
800     }
801   }
802
803   return out;
804 }
805
806 /*
807 =item fill_image(fill, x, y, width, channels, data, work)
808
809 =cut
810 */
811 static void
812 fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
813            int channels, i_color *data) {
814   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
815   i_img_dim i = 0;
816   i_color *out = data;
817   int want_channels = channels > 2 ? 4 : 2;
818   
819   if (f->has_matrix) {
820     /* the hard way */
821     while (i < width) {
822       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
823       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
824       double ix = floor(rx / f->src->xsize);
825       double iy = floor(ry / f->src->ysize);
826       i_color c[2][2];
827       i_color c2[2];
828       i_img_dim dy;
829
830       if (f->xoff) {
831         rx += iy * f->xoff;
832         ix = floor(rx / f->src->xsize);
833       }
834       else if (f->yoff) {
835         ry += ix * f->yoff;
836         iy = floor(ry / f->src->ysize);
837       }
838       rx -= ix * f->src->xsize;
839       ry -= iy * f->src->ysize;
840
841       for (dy = 0; dy < 2; ++dy) {
842         if ((i_img_dim)rx == f->src->xsize-1) {
843           i_gpix(f->src, f->src->xsize-1, ((i_img_dim)ry+dy) % f->src->ysize, &c[dy][0]);
844           i_gpix(f->src, 0, ((i_img_dim)ry+dy) % f->src->xsize, &c[dy][1]);
845         }
846         else {
847           i_glin(f->src, (i_img_dim)rx, (i_img_dim)rx+2, ((i_img_dim)ry+dy) % f->src->ysize, 
848                  c[dy]);
849         }
850         c2[dy] = interp_i_color(c[dy][0], c[dy][1], rx, f->src->channels);
851       }
852       *out++ = interp_i_color(c2[0], c2[1], ry, f->src->channels);
853       ++i;
854     }
855   }
856   else {
857     /* the easy way */
858     /* this should be possible to optimize to use i_glin() */
859     while (i < width) {
860       i_img_dim rx = x+i;
861       i_img_dim ry = y;
862       i_img_dim ix = rx / f->src->xsize;
863       i_img_dim iy = ry / f->src->ysize;
864
865       if (f->xoff) {
866         rx += iy * f->xoff;
867         ix = rx / f->src->xsize;
868       }
869       else if (f->yoff) {
870         ry += ix * f->yoff;
871         iy = ry / f->src->ysize;
872       }
873       rx -= ix * f->src->xsize;
874       ry -= iy * f->src->ysize;
875       i_gpix(f->src, rx, ry, out);
876       ++out;
877       ++i;
878     }
879   }
880   if (f->src->channels != want_channels)
881     i_adapt_colors(want_channels, f->src->channels, data, width);
882 }
883
884 /*
885 =item fill_imagef(fill, x, y, width, channels, data, work)
886
887 =cut
888 */
889 static void
890 fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
891             int channels, i_fcolor *data) {
892   struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
893   i_img_dim i = 0;
894   int want_channels = channels > 2 ? 4 : 2;
895   
896   if (f->has_matrix) {
897     i_fcolor *work_data = data;
898     /* the hard way */
899     while (i < width) {
900       double rx = f->matrix[0] * (x+i) + f->matrix[1] * y + f->matrix[2];
901       double ry = f->matrix[3] * (x+i) + f->matrix[4] * y + f->matrix[5];
902       double ix = floor(rx / f->src->xsize);
903       double iy = floor(ry / f->src->ysize);
904       i_fcolor c[2][2];
905       i_fcolor c2[2];
906       i_img_dim dy;
907
908       if (f->xoff) {
909         rx += iy * f->xoff;
910         ix = floor(rx / f->src->xsize);
911       }
912       else if (f->yoff) {
913         ry += ix * f->yoff;
914         iy = floor(ry / f->src->ysize);
915       }
916       rx -= ix * f->src->xsize;
917       ry -= iy * f->src->ysize;
918
919       for (dy = 0; dy < 2; ++dy) {
920         if ((i_img_dim)rx == f->src->xsize-1) {
921           i_gpixf(f->src, f->src->xsize-1, ((i_img_dim)ry+dy) % f->src->ysize, &c[dy][0]);
922           i_gpixf(f->src, 0, ((i_img_dim)ry+dy) % f->src->xsize, &c[dy][1]);
923         }
924         else {
925           i_glinf(f->src, (i_img_dim)rx, (i_img_dim)rx+2, ((i_img_dim)ry+dy) % f->src->ysize, 
926                  c[dy]);
927         }
928         c2[dy] = interp_i_fcolor(c[dy][0], c[dy][1], rx, f->src->channels);
929       }
930       *work_data++ = interp_i_fcolor(c2[0], c2[1], ry, f->src->channels);
931       ++i;
932     }
933   }
934   else {
935     i_fcolor *work_data = data;
936     /* the easy way */
937     /* this should be possible to optimize to use i_glin() */
938     while (i < width) {
939       i_img_dim rx = x+i;
940       i_img_dim ry = y;
941       i_img_dim ix = rx / f->src->xsize;
942       i_img_dim iy = ry / f->src->ysize;
943
944       if (f->xoff) {
945         rx += iy * f->xoff;
946         ix = rx / f->src->xsize;
947       }
948       else if (f->yoff) {
949         ry += ix * f->yoff;
950         iy = ry / f->src->xsize;
951       }
952       rx -= ix * f->src->xsize;
953       ry -= iy * f->src->ysize;
954       i_gpixf(f->src, rx, ry, work_data);
955       ++work_data;
956       ++i;
957     }
958   }
959   if (f->src->channels != want_channels)
960     i_adapt_fcolors(want_channels, f->src->channels, data, width);
961 }
962
963 static void 
964 fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
965              int channels, i_color *data) {
966   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
967   int alpha_chan = channels > 2 ? 3 : 1;
968   i_color *datap = data;
969   
970   (f->other_fill->f_fill_with_color)(f->other_fill, x, y, width, channels, data);
971   while (width--) {
972     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
973     if (new_alpha < 0) 
974       datap->channel[alpha_chan] = 0;
975     else if (new_alpha > 255)
976       datap->channel[alpha_chan] = 255;
977     else datap->channel[alpha_chan] = (int)(new_alpha + 0.5);
978
979     ++datap;
980   }
981 }
982 static void 
983 fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
984               int channels, i_fcolor *data) {
985   struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
986   int alpha_chan = channels > 2 ? 3 : 1;
987   i_fcolor *datap = data;
988   
989   (f->other_fill->f_fill_with_fcolor)(f->other_fill, x, y, width, channels, data);
990   
991   while (width--) {
992     double new_alpha = datap->channel[alpha_chan] * f->alpha_mult;
993     if (new_alpha < 0) 
994       datap->channel[alpha_chan] = 0;
995     else if (new_alpha > 1.0)
996       datap->channel[alpha_chan] = 1.0;
997     else datap->channel[alpha_chan] = new_alpha;
998
999     ++datap;
1000   }
1001 }
1002
1003 /*
1004 =back
1005
1006 =head1 AUTHOR
1007
1008 Tony Cook <tony@develop-help.com>
1009
1010 =head1 SEE ALSO
1011
1012 Imager(3)
1013
1014 =cut
1015 */