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