]> git.imager.perl.org - imager.git/blob - render.im
WIP, more coverage and fixes
[imager.git] / render.im
1 /*
2 Render utilities
3 */
4 #include "imager.h"
5
6 #define RENDER_MAGIC 0x765AE
7
8 typedef void (*render_color_f)(i_render *, i_img_dim, i_img_dim, i_img_dim, unsigned char const *src, i_color const *color);
9
10 #define i_has_alpha(channels) ((channels) == 2 || (channels) == 4)
11
12 #define i_color_channels(channels) (i_has_alpha(channels) ? (channels)-1 : (channels))
13
14 #code
15
16 static void IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width, unsigned char const *src, i_color const *color);
17 static void IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width, unsigned char const *src, i_color const *color);
18
19 static render_color_f IM_SUFFIX(render_color_tab)[] =
20   {
21     NULL,
22     IM_SUFFIX(render_color_13),
23     IM_SUFFIX(render_color_alpha),
24     IM_SUFFIX(render_color_13),
25     IM_SUFFIX(render_color_alpha),
26   };
27
28 static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
29 static void IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
30 /* the copy variant copies the source alpha to the the output alpha channel */
31 static void IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
32
33 static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
34 static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
35
36 #/code
37
38 /* 
39 =item i_render_new(im, width)
40 =category Blit tools
41
42 Allocate a new C<i_render> object and initialize it.
43
44 =cut
45 */
46
47 i_render *
48 i_render_new(i_img *im, i_img_dim width) {
49   i_render *r = mymalloc(sizeof(i_render));
50
51   i_render_init(r, im, width);
52
53   return r;
54 }
55
56 /*
57 =item i_render_delete(r)
58 =category Blit tools
59
60 Release an C<i_render> object.
61
62 =cut
63 */
64
65 void
66 i_render_delete(i_render *r) {
67   i_render_done(r);
68   myfree(r);
69 }
70
71 void
72 i_render_init(i_render *r, i_img *im, i_img_dim width) {
73   r->magic = RENDER_MAGIC;
74   r->im = im;
75   r->line_width = width;
76   r->line_8 = NULL;
77   r->line_double = NULL;
78   r->fill_width = width;
79   r->fill_line_8 = NULL;
80   r->fill_line_double = NULL;
81 }
82
83 void
84 i_render_done(i_render *r) {
85   if (r->line_8)
86     myfree(r->line_8);
87   if (r->line_double)
88     myfree(r->line_double);
89   if (r->fill_line_8)
90     myfree(r->fill_line_8);
91   if (r->fill_line_double)
92     myfree(r->fill_line_double);
93   r->magic = 0;
94 }
95
96 static void
97 alloc_line(i_render *r, i_img_dim width, i_img_dim eight_bit) {
98   if (width > r->line_width) {
99     i_img_dim new_width = r->line_width * 2;
100     if (new_width < width)
101       new_width = width;
102
103     if (eight_bit) {
104       if (r->line_8)
105         r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
106       else
107         r->line_8 = mymalloc(sizeof(i_color) * new_width);
108       if (r->line_double) {
109         myfree(r->line_double);
110         r->line_double = NULL;
111       }
112     }
113     else {
114       if (r->line_double)
115         r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
116       else
117         r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
118       if (r->line_8) {
119         myfree(r->line_8);
120         r->line_double = NULL;
121       }
122     }
123
124     r->line_width = new_width;
125   }
126   else {
127     if (eight_bit) {
128       if (!r->line_8)
129         r->line_8 = mymalloc(sizeof(i_color) * r->line_width);
130       if (r->line_double) {
131         myfree(r->line_double);
132         r->line_double = NULL;
133       }
134     }
135     else {
136       if (!r->line_double)
137         r->line_double = mymalloc(sizeof(i_fcolor) * r->line_width);
138       if (r->line_8) {
139         myfree(r->line_8);
140         r->line_8 = NULL;
141       }
142     }
143   }
144 }
145
146 static void
147 alloc_fill_line(i_render *r, i_img_dim width, int eight_bit) {
148   if (width > r->fill_width) {
149     i_img_dim new_width = r->fill_width * 2;
150     if (new_width < width)
151       new_width = width;
152
153     if (eight_bit) {
154       if (r->line_8)
155         r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
156       else
157         r->fill_line_8 = mymalloc(sizeof(i_color) * new_width);
158       if (r->fill_line_double) {
159         myfree(r->fill_line_double);
160         r->fill_line_double = NULL;
161       }
162     }
163     else {
164       if (r->fill_line_double)
165         r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
166       else
167         r->fill_line_double = mymalloc(sizeof(i_fcolor) * new_width);
168       if (r->fill_line_8) {
169         myfree(r->fill_line_8);
170         r->fill_line_double = NULL;
171       }
172     }
173
174     r->fill_width = new_width;
175   }
176   else {
177     if (eight_bit) {
178       if (!r->fill_line_8)
179         r->fill_line_8 = mymalloc(sizeof(i_color) * r->fill_width);
180       if (r->fill_line_double) {
181         myfree(r->fill_line_double);
182         r->fill_line_double = NULL;
183       }
184     }
185     else {
186       if (!r->fill_line_double)
187         r->fill_line_double = mymalloc(sizeof(i_fcolor) * r->fill_width);
188       if (r->fill_line_8) {
189         myfree(r->fill_line_8);
190         r->fill_line_8 = NULL;
191       }
192     }
193   }
194 }
195
196 /*
197 =item i_render_color(r, x, y, width, source, color)
198 =category Blit tools
199
200 Render the given color with the coverage specified by C<source[0]> to
201 C<source[width-1]>.
202
203 Renders in normal combine mode.
204
205 =cut
206 */
207
208 void
209 i_render_color(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
210                unsigned char const *src, i_color const *color) {
211   i_img *im = r->im;
212   if (y < 0 || y >= im->ysize)
213     return;
214   if (x < 0) {
215     width += x;
216     src -= x;
217     x = 0;
218   }
219   if (x + width > im->xsize) {
220     width = im->xsize - x;
221   }
222   if (x >= im->xsize || x + width <= 0 || width <= 0)
223     return;
224
225   /* avoid as much work as we can */
226   while (width > 0 && *src == 0) {
227     --width;
228     ++src;
229     ++x;
230   }
231   while (width > 0 && src[width-1] == 0) {
232     --width;
233   }
234   if (!width)
235     return;
236
237   alloc_line(r, width, r->im->bits <= 8);
238
239 #code r->im->bits <= 8
240   /*if (r->IM_SUFFIX(line) == NULL)
241     r->IM_SUFFIX(line) = mymalloc(sizeof(IM_COLOR) * r->width);*/
242   (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
243 #/code
244 }
245
246 /*
247 =item i_render_fill(r, x, y, width, source, fill)
248 =category Blit tools
249
250 Render the given fill with the coverage in C<source[0]> through
251 C<source[width-1]>.
252
253 =cut
254 */
255
256 void
257 i_render_fill(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
258               unsigned char const *src, i_fill_t *fill) {
259   i_img *im = r->im;
260   int fill_channels = im->channels;
261   
262   if (fill_channels == 1 || fill_channels == 3)
263     ++fill_channels;
264
265   if (y < 0 || y >= im->ysize)
266     return;
267   if (x < 0) {
268     width += x;
269     src -= x;
270     x = 0;
271   }
272   if (x + width > im->xsize) {
273     width = im->xsize - x;
274   }
275   if (x >= im->xsize || x + width <= 0 || width <= 0)
276     return;
277
278   if (src) {
279     /* avoid as much work as we can */
280     while (width > 0 && *src == 0) {
281       --width;
282       ++src;
283       ++x;
284     }
285     while (width > 0 && src[width-1] == 0) {
286       --width;
287     }
288   }
289   if (!width)
290     return;
291
292   alloc_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
293   alloc_fill_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
294
295 #code r->im->bits <= 8 && fill->f_fill_with_color
296   if (IM_FILL_COMBINE(fill)) {
297     IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
298     IM_COLOR *destc = r->IM_SUFFIX(line);
299     IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
300     if (src) {
301       unsigned char const *srcc = src;
302       IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
303       i_img_dim work_width = width;
304       while (work_width) {
305         if (*srcc == 0) {
306           fillc->channel[fill_channels-1] = 0;
307         }
308         else if (*srcc != 255) {
309           fillc->channel[fill_channels-1] =
310             fillc->channel[fill_channels-1] * *srcc / 255;
311         }
312         --work_width;
313         ++srcc;
314         ++fillc;
315       }
316     }
317     IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
318     IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
319   }
320   else {
321     if (src) {
322       i_img_dim work_width = width;
323       IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
324       IM_COLOR *destc = r->IM_SUFFIX(line);
325       int ch;
326
327       IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
328       IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
329       while (work_width) {
330         if (*src == 255) {
331           /* just replace it */
332           *destc = *srcc;
333         }
334         else if (*src) {
335           for (ch = 0; ch < im->channels; ++ch) {
336             IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
337                               + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
338             destc->channel[ch] = IM_LIMIT(work);
339           }
340         }
341         
342         ++srcc;
343         ++destc;
344         ++src;
345         --work_width;
346       }
347     }
348     else { /* if (src) */
349       IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(line));
350     }
351   }
352   IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
353 #/code
354 }
355
356 #if 0
357
358 /* for debuggin */
359
360 static void
361 dump_src(const char *note, unsigned char const *src, i_img_dim width) {
362   i_img_dim i;
363   printf("%s - %p/%" i_DF "\n", note, src, i_DFc(width));
364   for (i = 0; i < width; ++i) {
365     printf("%02x ", src[i]);
366   }
367   putchar('\n');
368 }
369
370 #endif
371
372 #code
373
374 /*
375 =item i_render_line(r, x, y, width, source, fill)
376 =category Blit tools
377
378 Render the given fill with the coverage in C<source[0]> through
379 C<source[width-1]>.
380
381 =cut
382
383 =item i_render_linef(r, x, y, width, source, fill)
384 =category Blit tools
385
386 Render the given fill with the coverage in C<source[0]> through
387 C<source[width-1]>.
388
389 =cut
390 */
391
392 void
393 IM_RENDER_LINE(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
394                const IM_SAMPLE_T *src, IM_COLOR *line,
395                IM_FILL_COMBINE_F combine) {
396   i_img *im = r->im;
397   int src_chans = im->channels;
398
399   /* src must always have an alpha channel */
400   if (src_chans == 1 || src_chans == 3)
401     ++src_chans;
402
403   if (y < 0 || y >= im->ysize)
404     return;
405   if (x < 0) {
406     src -= x;
407     line -= x;
408     width += x;
409     x = 0;
410   }
411   if (x + width > im->xsize)
412     width = r->im->xsize - x;
413
414 #ifdef IM_EIGHT_BIT
415   alloc_line(r, width, 1);
416 #else
417   alloc_line(r, width, 0);
418 #endif
419
420   if (combine) {
421     if (src) {
422       i_img_dim work_width = width;
423       IM_COLOR *linep = line;
424       const IM_SAMPLE_T *srcp = src;
425       int alpha_chan = src_chans - 1;
426       
427       while (work_width) {
428         if (*srcp) {
429           if (*srcp != IM_SAMPLE_MAX) 
430             linep->channel[alpha_chan] = 
431               linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
432         }
433         else {
434           linep->channel[alpha_chan] = 0;
435         }
436         --work_width;
437         ++srcp;
438         ++linep;
439       }
440     }
441     IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
442     combine(r->IM_SUFFIX(line), line, im->channels, width);
443     IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
444   }
445   else {
446     if (src) {
447       i_img_dim work_width = width;
448       IM_COLOR *srcc = line;
449       IM_COLOR *destc = r->IM_SUFFIX(line);
450
451       IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
452       while (work_width) {
453         if (*src == 255) {
454           /* just replace it */
455           *destc = *srcc;
456         }
457         else if (*src) {
458           int ch;
459           for (ch = 0; ch < im->channels; ++ch) {
460             IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
461                               + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
462             destc->channel[ch] = IM_LIMIT(work);
463           }
464         }
465         
466         ++srcc;
467         ++destc;
468         ++src;
469         --work_width;
470       }
471       IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
472     }
473     else {
474       IM_PLIN(im, x, x+width, y, line);
475     }
476   }
477 }
478
479 static
480 void
481 IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y,
482                            i_img_dim width, unsigned char const *src,
483                            i_color const *color) {
484   i_img *im = r->im;
485   IM_COLOR *linep = r->IM_SUFFIX(line);
486   int ch, channels = im->channels;
487   i_img_dim fetch_offset;
488 #undef STORE_COLOR
489 #ifdef IM_EIGHT_BIT
490 #define STORE_COLOR (*color)
491 #else
492   i_fcolor fcolor;
493
494   for (ch = 0; ch < channels; ++ch) {
495     fcolor.channel[ch] = color->channel[ch] / 255.0;
496   }
497 #define STORE_COLOR fcolor
498 #endif
499  
500   fetch_offset = 0;
501   while (fetch_offset < width && *src == 0xFF) {
502     *linep++ = STORE_COLOR;
503     ++src;
504     ++fetch_offset;
505   }
506   IM_GLIN(im, x+fetch_offset, x+width, y, linep);
507   while (fetch_offset < width) {
508 #ifdef IM_EIGHT_BIT
509     IM_WORK_T alpha = *src++;
510 #else
511     IM_WORK_T alpha = *src++ / 255.0;
512 #endif
513     if (alpha == IM_SAMPLE_MAX)
514       *linep = STORE_COLOR;
515     else if (alpha) {
516       for (ch = 0; ch < channels; ++ch) {
517         linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha) 
518                               + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
519       }
520     }
521     ++linep;
522     ++fetch_offset;
523   }
524   IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
525 }
526
527 static
528 void
529 IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
530                               i_img_dim width, unsigned char const *src,
531                               i_color const *color) {
532   IM_COLOR *linep = r->IM_SUFFIX(line);
533   int ch;
534   int alpha_channel = r->im->channels - 1;
535   i_img_dim fetch_offset;
536 #undef STORE_COLOR
537 #ifdef IM_EIGHT_BIT
538 #define STORE_COLOR (*color)
539 #else
540   i_fcolor fcolor;
541
542   for (ch = 0; ch < r->im->channels; ++ch) {
543     fcolor.channel[ch] = color->channel[ch] / 255.0;
544   }
545 #define STORE_COLOR fcolor
546 #endif
547
548   fetch_offset = 0;
549   while (fetch_offset < width && *src == 0xFF) {
550     *linep++ = STORE_COLOR;
551     ++src;
552     ++fetch_offset;
553   }
554   IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
555   while (fetch_offset < width) {
556 #ifdef IM_EIGHT_BIT
557     IM_WORK_T src_alpha = *src++;
558 #else
559     IM_WORK_T src_alpha = *src++ / 255.0;
560 #endif
561     if (src_alpha == IM_SAMPLE_MAX)
562       *linep = STORE_COLOR;
563     else if (src_alpha) {
564       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
565       IM_WORK_T orig_alpha = linep->channel[alpha_channel];
566       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
567       for (ch = 0; ch < alpha_channel; ++ch) {
568         linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
569                                + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
570                                ) / dest_alpha;
571       }
572       linep->channel[alpha_channel] = dest_alpha;
573     }
574     ++linep;
575     ++fetch_offset;
576   }
577   IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
578 #undef STORE_COLOR
579 }
580
581 /* combine a line of image data with an output line, both the input
582    and output lines include an alpha channel.
583
584    Both input and output lines have I<channels> of data, channels
585    should be either 2 or 4.
586 */
587
588 static void
589 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, 
590                               int channels, i_img_dim count) {
591   int ch;
592   int alpha_channel = channels - 1;
593   
594   while (count) {
595     IM_WORK_T src_alpha = in->channel[alpha_channel];
596       
597     if (src_alpha == IM_SAMPLE_MAX)
598       *out = *in;
599     else if (src_alpha) {
600       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
601       IM_WORK_T orig_alpha = out->channel[alpha_channel];
602       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
603         
604       for (ch = 0; ch < alpha_channel; ++ch) {
605         out->channel[ch] = ( src_alpha * in->channel[ch]
606                              + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
607                              ) / dest_alpha;
608       }
609       out->channel[alpha_channel] = dest_alpha;
610     }
611
612     ++out;
613     ++in;
614     --count;
615   }
616 }
617
618 /* combine a line of image data with an output line.  The input line
619    includes an alpha channel, the output line has no alpha channel.
620    
621    The input line has I<channels>+1 of color data.  The output line
622    has I<channels> of color data.
623 */
624
625 static void
626 IM_SUFFIX(combine_line_noalpha)
627      (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
628   int ch;
629
630   while (count) {
631     IM_WORK_T src_alpha = in->channel[channels];
632     
633     if (src_alpha == IM_SAMPLE_MAX)
634       *out = *in;
635     else if (src_alpha) {
636       IM_WORK_T remains;
637       
638       remains = IM_SAMPLE_MAX - src_alpha;
639       for (ch = 0; ch < channels; ++ch) {
640         out->channel[ch] = ( in->channel[ch] * src_alpha
641                              + out->channel[ch] * remains) / IM_SAMPLE_MAX;
642       }
643     }
644     
645     ++out;
646     ++in;
647     --count;
648   }
649 }
650
651 /* combine a line of image data with an output line, both the input
652    and output lines include an alpha channel.
653
654    Both input and output lines have I<channels> of data, channels
655    should be either 2 or 4.
656
657    This variant does not modify the output alpha channel.
658 */
659
660 static void
661 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, 
662                                    int channels, i_img_dim count) {
663   int ch;
664   int alpha_channel = channels - 1;
665   
666   while (count) {
667     IM_WORK_T src_alpha = in->channel[alpha_channel];
668       
669     if (src_alpha == IM_SAMPLE_MAX)
670       *out = *in;
671     else if (src_alpha) {
672       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
673       IM_WORK_T orig_alpha = out->channel[alpha_channel];
674       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
675         
676       for (ch = 0; ch < alpha_channel; ++ch) {
677         out->channel[ch] = ( src_alpha * in->channel[ch]
678                              + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
679                              ) / dest_alpha;
680       }
681     }
682
683     ++out;
684     ++in;
685     --count;
686   }
687 }
688
689 static void
690 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
691   if (channels == 2 || channels == 4)
692     IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
693   else
694     IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
695 }
696
697 static void
698 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
699   if (channels == 2 || channels == 4)
700     IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
701   else
702     IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
703 }
704
705 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
706 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
707 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
708 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
709 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
710 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
711 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
712 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
713 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
714 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
715 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
716 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
717
718 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
719 {
720   NULL,
721   IM_SUFFIX(combine_alphablend),
722   IM_SUFFIX(combine_mult),
723   IM_SUFFIX(combine_dissolve),
724   IM_SUFFIX(combine_add),
725   IM_SUFFIX(combine_subtract),
726   IM_SUFFIX(combine_diff),
727   IM_SUFFIX(combine_lighten),
728   IM_SUFFIX(combine_darken),
729   IM_SUFFIX(combine_hue),
730   IM_SUFFIX(combine_sat),
731   IM_SUFFIX(combine_value),
732   IM_SUFFIX(combine_color)
733 };
734
735 #/code
736
737 /*
738 =item i_get_combine(combine, color_func, fcolor_func)
739
740 =cut
741 */
742
743 void i_get_combine(int combine, i_fill_combine_f *color_func, 
744                    i_fill_combinef_f *fcolor_func) {
745   if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
746     combine = 0;
747
748   *color_func = combines_8[combine];
749   *fcolor_func = combines_double[combine];
750 }
751
752 #code
753
754 /*
755   Three good references for implementing combining modes:
756
757   http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
758   referenced as [svg1.2]
759
760   http://gimp-savvy.com/BOOK/index.html?node55.html
761   ("The Blending Modes", if it changes)
762   referenced as [savvy]
763
764   http://www.pegtop.net/delphi/articles/blendmodes/
765   referenced as [pegtop]
766
767   Where differences exist, I follow the SVG practice, the gimp
768   practice, and lastly pegtop.
769 */
770
771
772 static void 
773 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
774   IM_SUFFIX(combine_line)(out, in, channels, count);
775 }
776
777 /*
778 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
779 Da'  = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
780      = Sa + Da - Sa.Da
781
782 When Da=1
783
784 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
785  */
786 static void
787 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
788   int ch;
789   IM_COLOR *inp = in;
790   IM_COLOR *outp = out;
791   i_img_dim work_count = count;
792   int color_channels = i_color_channels(channels);
793
794   if (i_has_alpha(channels)) {
795     while (work_count--) {
796       IM_WORK_T orig_alpha = outp->channel[color_channels];
797       IM_WORK_T src_alpha = inp->channel[color_channels];
798       
799       if (src_alpha) {
800         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
801           - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
802         
803         for (ch = 0; ch < color_channels; ++ch) { 
804           outp->channel[ch] = 
805             (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
806              * orig_alpha
807              + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
808              + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
809             / IM_SAMPLE_MAX / dest_alpha;
810         }
811         outp->channel[color_channels] = dest_alpha;
812       }
813       ++outp;
814       ++inp;
815     }
816   }
817   else {
818     while (work_count--) {
819       IM_WORK_T src_alpha = inp->channel[color_channels];
820       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
821       
822       if (src_alpha) {
823         for (ch = 0; ch < color_channels; ++ch) { 
824           outp->channel[ch] = 
825             (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
826              + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
827         }
828       }
829       ++outp;
830       ++inp;
831     }
832   }
833 }
834
835 static void 
836 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
837   int color_channels = i_color_channels(channels);
838   int ch;
839
840   if (i_has_alpha(channels)) {
841     while (count--) {
842       if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
843         for (ch = 0; ch < color_channels; ++ch) {
844           out->channel[ch] = in->channel[ch];
845         }
846         out->channel[color_channels] = IM_SAMPLE_MAX;
847       }
848       ++out;
849       ++in;
850     }
851   }
852   else {
853     while (count--) {
854       if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
855         for (ch = 0; ch < color_channels; ++ch) {
856           out->channel[ch] = in->channel[ch];
857         }
858       }
859       ++out;
860       ++in;
861     }
862   }
863 }
864
865 /*
866 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
867      = Sca + Dca
868 Da'  = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
869      = Sa + Da
870 */
871
872 static void
873 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
874   int ch;
875   int color_channels = i_color_channels(channels);
876   i_img_dim work_count = count;
877   IM_COLOR *inp = in;
878   IM_COLOR *outp = out;
879
880   if (i_has_alpha(channels)) {
881     while (work_count--) {
882       IM_WORK_T src_alpha = inp->channel[color_channels];
883       if (src_alpha) {
884         IM_WORK_T orig_alpha = outp->channel[color_channels];
885         IM_WORK_T dest_alpha = src_alpha + orig_alpha;
886         if (dest_alpha > IM_SAMPLE_MAX)
887           dest_alpha = IM_SAMPLE_MAX;
888         for (ch = 0; ch < color_channels; ++ch) { 
889           IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
890           if (total > IM_SAMPLE_MAX)
891             total = IM_SAMPLE_MAX;
892           outp->channel[ch] = total;
893         }
894         outp->channel[color_channels] = dest_alpha;
895       }
896       
897       ++outp;
898       ++inp;
899     }
900   }
901   else {
902     while (work_count--) {
903       IM_WORK_T src_alpha = inp->channel[color_channels];
904       if (src_alpha) {
905         for (ch = 0; ch < color_channels; ++ch) { 
906           IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
907           if (total > IM_SAMPLE_MAX)
908             total = IM_SAMPLE_MAX;
909           outp->channel[ch] = total;
910         } 
911       }
912       
913       ++outp;
914       ++inp;
915     }
916   }
917 }
918
919 /* 
920    [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
921    it as max(A-B, 0). [svg1.2] doesn't cover it.
922
923    [savvy] doesn't document how it works with an alpha channel.  GIMP
924    actually seems to calculate the final value then use the alpha
925    channel to apply that to the target.
926  */
927 static void
928 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
929   int ch;
930   IM_COLOR const *inp = in;
931   IM_COLOR *outp = out;
932   i_img_dim work_count = count;
933   int color_channels = i_color_channels(channels);
934
935   if (i_has_alpha(channels)) {
936     while (work_count--) {
937       IM_WORK_T src_alpha = inp->channel[color_channels];
938       if (src_alpha) {
939         IM_WORK_T orig_alpha = outp->channel[color_channels];
940         IM_WORK_T dest_alpha = src_alpha + orig_alpha;
941         if (dest_alpha > IM_SAMPLE_MAX)
942           dest_alpha = IM_SAMPLE_MAX;
943         for (ch = 0; ch < color_channels; ++ch) { 
944           IM_WORK_T total = 
945             (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha) 
946             / dest_alpha;
947           if (total < 0)
948             total = 0;
949           outp->channel[ch] = total;
950         }
951         outp->channel[color_channels] = dest_alpha;
952       }
953       ++outp;
954       ++inp;
955     }
956   }
957   else {
958     while (work_count--) {
959       IM_WORK_T src_alpha = inp->channel[color_channels];
960       if (src_alpha) {
961         for (ch = 0; ch < color_channels; ++ch) { 
962           IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
963           if (total < 0)
964             total = 0;
965           outp->channel[ch] = total;
966         } 
967       }
968       ++outp;
969       ++inp;
970     }
971   }
972 }
973
974 #ifdef IM_EIGHT_BIT
975 #define IM_abs(x) abs(x)
976 #else
977 #define IM_abs(x) fabs(x)
978 #endif
979
980 /*
981 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
982      = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
983 Da'  = Sa + Da - Sa.Da 
984 */
985 static void
986 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
987   int ch;
988   IM_COLOR const *inp = in;
989   IM_COLOR *outp = out;
990   i_img_dim work_count = count;
991   int color_channels = i_color_channels(channels);
992
993   if (i_has_alpha(channels)) {
994     while (work_count--) {
995       IM_WORK_T src_alpha = inp->channel[color_channels];
996       if (src_alpha) {
997         IM_WORK_T orig_alpha = outp->channel[color_channels];
998         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
999           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1000         for (ch = 0; ch < color_channels; ++ch) {
1001           IM_WORK_T src = inp->channel[ch] * src_alpha;
1002           IM_WORK_T orig = outp->channel[ch] * orig_alpha;
1003           IM_WORK_T src_da = src * orig_alpha;
1004           IM_WORK_T dest_sa = orig * src_alpha;
1005           IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1006           outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1007         }
1008         outp->channel[color_channels] = dest_alpha;
1009       }
1010       ++inp;
1011       ++outp;
1012     }
1013   }
1014   else {
1015     while (work_count--) {
1016       IM_WORK_T src_alpha = inp->channel[color_channels];
1017       if (src_alpha) {
1018         for (ch = 0; ch < color_channels; ++ch) {
1019           IM_WORK_T src = inp->channel[ch] * src_alpha;
1020           IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1021           IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1022           IM_WORK_T dest_sa = orig * src_alpha;
1023           IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1024           outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1025         }
1026       }
1027       ++inp;
1028       ++outp;
1029     }
1030   }
1031 }
1032
1033 #undef IM_abs
1034
1035 /*
1036   Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1037   Da' = Sa + Da - Sa.Da
1038
1039   To hoist some code:
1040
1041   Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1042        = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1043
1044   When Da=1:
1045
1046   Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1047        = min(Sca, Dc.Sa) + Dc(1-Sa)
1048        = Sa.min(Sc, Dc) + Dc - Dc.Sa
1049   Da'  = Sa + 1 - Sa.1
1050        = 1
1051  */
1052 static void 
1053 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels, 
1054                           i_img_dim count) {
1055   int ch;
1056   IM_COLOR const *inp = in;
1057   IM_COLOR *outp = out;
1058   i_img_dim work_count = count;
1059   int color_channels = i_color_channels(channels);
1060
1061   if (i_has_alpha(channels)) {
1062     while (work_count--) {
1063       IM_WORK_T src_alpha = inp->channel[color_channels];
1064
1065       if (src_alpha) {
1066         IM_WORK_T orig_alpha = outp->channel[color_channels];
1067         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
1068           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1069         for (ch = 0; ch < color_channels; ++ch) { 
1070           IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1071           IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1072           IM_WORK_T ScaDa = Sca * orig_alpha;
1073           IM_WORK_T DcaSa = Dca * src_alpha;
1074           IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1075           outp->channel[ch] = 
1076             ( 
1077              minc + (Sca + Dca) * IM_SAMPLE_MAX
1078              - ScaDa - DcaSa
1079              ) / (IM_SAMPLE_MAX * dest_alpha);
1080         } 
1081         outp->channel[color_channels] = dest_alpha;
1082       }
1083       ++outp;
1084       ++inp;
1085     }
1086   }
1087   else {
1088     while (work_count--) {
1089       IM_WORK_T src_alpha = inp->channel[color_channels];
1090
1091       if (src_alpha) {
1092         for (ch = 0; ch < color_channels; ++ch) { 
1093           IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1094             ? outp->channel[ch] : inp->channel[ch];
1095           outp->channel[ch] = 
1096             ( 
1097              src_alpha * minc + 
1098              outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1099              ) / IM_SAMPLE_MAX;
1100         } 
1101       }
1102       ++outp;
1103       ++inp;
1104     }
1105   }
1106 }
1107
1108 static void 
1109 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1110   int ch;
1111   IM_COLOR const *inp = in;
1112   IM_COLOR *outp = out;
1113   i_img_dim work_count = count;
1114   int color_channels = i_color_channels(channels);
1115
1116   if (i_has_alpha(channels)) {
1117     while (work_count--) {
1118       IM_WORK_T src_alpha = inp->channel[color_channels];
1119
1120       if (src_alpha) {
1121         IM_WORK_T orig_alpha = outp->channel[color_channels];
1122         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
1123           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1124         for (ch = 0; ch < color_channels; ++ch) { 
1125           IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1126           IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1127           IM_WORK_T ScaDa = Sca * orig_alpha;
1128           IM_WORK_T DcaSa = Dca * src_alpha;
1129           IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1130           outp->channel[ch] = 
1131             ( 
1132              maxc + (Sca + Dca) * IM_SAMPLE_MAX
1133              - ScaDa - DcaSa
1134              ) / (IM_SAMPLE_MAX * dest_alpha);
1135         } 
1136         outp->channel[color_channels] = dest_alpha;
1137       }
1138       ++outp;
1139       ++inp;
1140     }
1141   }
1142   else {
1143     while (work_count--) {
1144       IM_WORK_T src_alpha = inp->channel[color_channels];
1145
1146       if (src_alpha) {
1147         for (ch = 0; ch < color_channels; ++ch) { 
1148           IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1149             ? outp->channel[ch] : inp->channel[ch];
1150           outp->channel[ch] = 
1151             ( 
1152              src_alpha * maxc + 
1153              outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1154              ) / IM_SAMPLE_MAX;
1155         } 
1156       }
1157       ++outp;
1158       ++inp;
1159     }
1160   }
1161 }
1162
1163 #if IM_EIGHT_BIT
1164 #define IM_RGB_TO_HSV i_rgb_to_hsv
1165 #define IM_HSV_TO_RGB i_hsv_to_rgb
1166 #else
1167 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1168 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1169 #endif
1170
1171 static void 
1172 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1173   if (channels > 2) {
1174     IM_COLOR *inp = in;
1175     IM_COLOR const *outp = out;
1176     i_img_dim work_count = count;
1177
1178     if (i_has_alpha(channels)) {
1179       while (work_count--) {
1180         IM_COLOR c = *inp;
1181         IM_RGB_TO_HSV(&c);
1182         /* only transfer hue if there's saturation */
1183         if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1184           *inp = *outp;
1185           IM_RGB_TO_HSV(inp);
1186           /* and no point in setting the target hue if the target has no sat */
1187           if (inp->channel[1]) {
1188             inp->channel[0] = c.channel[0];
1189             IM_HSV_TO_RGB(inp);
1190             inp->channel[3] = c.channel[3];
1191           }
1192           else {
1193             inp->channel[3] = 0;
1194           }
1195         }
1196         else {
1197           inp->channel[3] = 0;
1198         }
1199         
1200         ++outp;
1201         ++inp;
1202       }
1203     }
1204     else {
1205       while (work_count--) {
1206         IM_COLOR c = *inp;
1207         IM_RGB_TO_HSV(&c);
1208         /* only transfer hue if there's saturation */
1209         if (c.channel[1] && inp->channel[3]) {
1210           *inp = *outp;
1211           IM_RGB_TO_HSV(inp);
1212           /* and no point in setting the target hue if the target has no sat */
1213           if (inp->channel[1]) {
1214             inp->channel[0] = c.channel[0];
1215             IM_HSV_TO_RGB(inp);
1216             inp->channel[3] = c.channel[3];
1217           }
1218         }
1219         else {
1220           inp->channel[3] = 0;
1221         }
1222         
1223         ++outp;
1224         ++inp;
1225       }
1226     }
1227
1228     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1229   }
1230 }
1231
1232 static void
1233 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1234   if (channels > 2) {
1235     IM_COLOR *inp = in;
1236     IM_COLOR const *outp = out;
1237     i_img_dim work_count = count;
1238
1239     while (work_count--) {
1240       IM_COLOR c = *inp;
1241       *inp = *outp;
1242       IM_RGB_TO_HSV(&c);
1243       IM_RGB_TO_HSV(inp);
1244       inp->channel[1] = c.channel[1];
1245       IM_HSV_TO_RGB(inp);
1246       inp->channel[3] = c.channel[3];
1247       ++outp;
1248       ++inp;
1249     }
1250
1251     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1252   }
1253 }
1254
1255 static void 
1256 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1257   if (channels > 2) {
1258     IM_COLOR *inp = in;
1259     IM_COLOR const *outp = out;
1260     i_img_dim work_count = count;
1261
1262     while (work_count--) {
1263       IM_COLOR c = *inp;
1264       *inp = *outp;
1265       IM_RGB_TO_HSV(&c);
1266       IM_RGB_TO_HSV(inp);
1267       inp->channel[2] = c.channel[2];
1268       IM_HSV_TO_RGB(inp);
1269       inp->channel[3] = c.channel[3];
1270       ++outp;
1271       ++inp;
1272     }
1273   }
1274
1275   /* all images have a "value channel" - for greyscale it's the only
1276      colour channel */
1277   IM_SUFFIX(combine_line_na)(out, in, channels, count);
1278 }
1279
1280 static void 
1281 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1282   if (channels > 2) {
1283     IM_COLOR *inp = in;
1284     IM_COLOR const *outp = out;
1285     i_img_dim work_count = count;
1286
1287     while (work_count--) {
1288       IM_COLOR c = *inp;
1289       *inp = *outp;
1290       IM_RGB_TO_HSV(&c);
1291       IM_RGB_TO_HSV(inp);
1292       inp->channel[0] = c.channel[0];
1293       inp->channel[1] = c.channel[1];
1294       IM_HSV_TO_RGB(inp);
1295       inp->channel[3] = c.channel[3];
1296       ++outp;
1297       ++inp;
1298     }
1299
1300     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1301   }
1302 }
1303
1304 #undef IM_RGB_TO_HSV
1305 #undef IM_HSV_TO_RGB
1306
1307 #/code