]> git.imager.perl.org - imager.git/blob - render.im
[rt.cpan.org #65385] Patch for Imager::Color->hsv
[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 static void
357 dump_src(const char *note, unsigned char const *src, i_img_dim width) {
358   i_img_dim i;
359   printf("%s - %p/%d\n", note, src, width);
360   for (i = 0; i < width; ++i) {
361     printf("%02x ", src[i]);
362   }
363   putchar('\n');
364 }
365
366 #code
367
368 /*
369 =item i_render_line(r, x, y, width, source, fill)
370 =category Blit tools
371
372 Render the given fill with the coverage in C<source[0]> through
373 C<source[width-1]>.
374
375 =cut
376
377 =item i_render_linef(r, x, y, width, source, fill)
378 =category Blit tools
379
380 Render the given fill with the coverage in C<source[0]> through
381 C<source[width-1]>.
382
383 =cut
384 */
385
386 void
387 IM_RENDER_LINE(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
388                const IM_SAMPLE_T *src, IM_COLOR *line,
389                IM_FILL_COMBINE_F combine) {
390   i_img *im = r->im;
391   int src_chans = im->channels;
392
393   /* src must always have an alpha channel */
394   if (src_chans == 1 || src_chans == 3)
395     ++src_chans;
396
397   if (y < 0 || y >= im->ysize)
398     return;
399   if (x < 0) {
400     src -= x;
401     line -= x;
402     width += x;
403     x = 0;
404   }
405   if (x + width > im->xsize)
406     width = r->im->xsize - x;
407
408 #ifdef IM_EIGHT_BIT
409   alloc_line(r, width, 1);
410 #else
411   alloc_line(r, width, 0);
412 #endif
413
414   if (combine) {
415     if (src) {
416       i_img_dim work_width = width;
417       IM_COLOR *linep = line;
418       const IM_SAMPLE_T *srcp = src;
419       int alpha_chan = src_chans - 1;
420       
421       while (work_width) {
422         if (*srcp) {
423           if (*srcp != IM_SAMPLE_MAX) 
424             linep->channel[alpha_chan] = 
425               linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
426         }
427         else {
428           linep->channel[alpha_chan] = 0;
429         }
430         --work_width;
431         ++srcp;
432         ++linep;
433       }
434     }
435     IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
436     combine(r->IM_SUFFIX(line), line, im->channels, width);
437     IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
438   }
439   else {
440     if (src) {
441       i_img_dim work_width = width;
442       IM_COLOR *srcc = line;
443       IM_COLOR *destc = r->IM_SUFFIX(line);
444
445       IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
446       while (work_width) {
447         if (*src == 255) {
448           /* just replace it */
449           *destc = *srcc;
450         }
451         else if (*src) {
452           int ch;
453           for (ch = 0; ch < im->channels; ++ch) {
454             IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
455                               + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
456             destc->channel[ch] = IM_LIMIT(work);
457           }
458         }
459         
460         ++srcc;
461         ++destc;
462         ++src;
463         --work_width;
464       }
465       IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
466     }
467     else {
468       IM_PLIN(im, x, x+width, y, line);
469     }
470   }
471 }
472
473 static
474 void
475 IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y,
476                            i_img_dim width, unsigned char const *src,
477                            i_color const *color) {
478   i_img *im = r->im;
479   IM_COLOR *linep = r->IM_SUFFIX(line);
480   int ch, channels = im->channels;
481   i_img_dim fetch_offset;
482 #undef STORE_COLOR
483 #ifdef IM_EIGHT_BIT
484 #define STORE_COLOR (*color)
485 #else
486   i_fcolor fcolor;
487
488   for (ch = 0; ch < channels; ++ch) {
489     fcolor.channel[ch] = color->channel[ch] / 255.0;
490   }
491 #define STORE_COLOR fcolor
492 #endif
493  
494   fetch_offset = 0;
495   while (fetch_offset < width && *src == 0xFF) {
496     *linep++ = STORE_COLOR;
497     ++src;
498     ++fetch_offset;
499   }
500   IM_GLIN(im, x+fetch_offset, x+width, y, linep);
501   while (fetch_offset < width) {
502 #ifdef IM_EIGHT_BIT
503     IM_WORK_T alpha = *src++;
504 #else
505     IM_WORK_T alpha = *src++ / 255.0;
506 #endif
507     if (alpha == IM_SAMPLE_MAX)
508       *linep = STORE_COLOR;
509     else if (alpha) {
510       for (ch = 0; ch < channels; ++ch) {
511         linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha) 
512                               + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
513       }
514     }
515     ++linep;
516     ++fetch_offset;
517   }
518   IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
519 }
520
521 static
522 void
523 IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
524                               i_img_dim width, unsigned char const *src,
525                               i_color const *color) {
526   IM_COLOR *linep = r->IM_SUFFIX(line);
527   int ch;
528   int alpha_channel = r->im->channels - 1;
529   i_img_dim fetch_offset;
530 #undef STORE_COLOR
531 #ifdef IM_EIGHT_BIT
532 #define STORE_COLOR (*color)
533 #else
534   i_fcolor fcolor;
535
536   for (ch = 0; ch < r->im->channels; ++ch) {
537     fcolor.channel[ch] = color->channel[ch] / 255.0;
538   }
539 #define STORE_COLOR fcolor
540 #endif
541
542   fetch_offset = 0;
543   while (fetch_offset < width && *src == 0xFF) {
544     *linep++ = STORE_COLOR;
545     ++src;
546     ++fetch_offset;
547   }
548   IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
549   while (fetch_offset < width) {
550 #ifdef IM_EIGHT_BIT
551     IM_WORK_T src_alpha = *src++;
552 #else
553     IM_WORK_T src_alpha = *src++ / 255.0;
554 #endif
555     if (src_alpha == IM_SAMPLE_MAX)
556       *linep = STORE_COLOR;
557     else if (src_alpha) {
558       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
559       IM_WORK_T orig_alpha = linep->channel[alpha_channel];
560       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
561       for (ch = 0; ch < alpha_channel; ++ch) {
562         linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
563                                + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
564                                ) / dest_alpha;
565       }
566       linep->channel[alpha_channel] = dest_alpha;
567     }
568     ++linep;
569     ++fetch_offset;
570   }
571   IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
572 #undef STORE_COLOR
573 }
574
575 /* combine a line of image data with an output line, both the input
576    and output lines include an alpha channel.
577
578    Both input and output lines have I<channels> of data, channels
579    should be either 2 or 4.
580 */
581
582 static void
583 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, 
584                               int channels, i_img_dim count) {
585   int ch;
586   int alpha_channel = channels - 1;
587   
588   while (count) {
589     IM_WORK_T src_alpha = in->channel[alpha_channel];
590       
591     if (src_alpha == IM_SAMPLE_MAX)
592       *out = *in;
593     else if (src_alpha) {
594       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
595       IM_WORK_T orig_alpha = out->channel[alpha_channel];
596       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
597         
598       for (ch = 0; ch < alpha_channel; ++ch) {
599         out->channel[ch] = ( src_alpha * in->channel[ch]
600                              + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
601                              ) / dest_alpha;
602       }
603       out->channel[alpha_channel] = dest_alpha;
604     }
605
606     ++out;
607     ++in;
608     --count;
609   }
610 }
611
612 /* combine a line of image data with an output line.  The input line
613    includes an alpha channel, the output line has no alpha channel.
614    
615    The input line has I<channels>+1 of color data.  The output line
616    has I<channels> of color data.
617 */
618
619 static void
620 IM_SUFFIX(combine_line_noalpha)
621      (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
622   int ch;
623
624   while (count) {
625     IM_WORK_T src_alpha = in->channel[channels];
626     
627     if (src_alpha == IM_SAMPLE_MAX)
628       *out = *in;
629     else if (src_alpha) {
630       IM_WORK_T remains;
631       
632       remains = IM_SAMPLE_MAX - src_alpha;
633       for (ch = 0; ch < channels; ++ch) {
634         out->channel[ch] = ( in->channel[ch] * src_alpha
635                              + out->channel[ch] * remains) / IM_SAMPLE_MAX;
636       }
637     }
638     
639     ++out;
640     ++in;
641     --count;
642   }
643 }
644
645 /* combine a line of image data with an output line, both the input
646    and output lines include an alpha channel.
647
648    Both input and output lines have I<channels> of data, channels
649    should be either 2 or 4.
650
651    This variant does not modify the output alpha channel.
652 */
653
654 static void
655 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, 
656                                    int channels, i_img_dim count) {
657   int ch;
658   int alpha_channel = channels - 1;
659   
660   while (count) {
661     IM_WORK_T src_alpha = in->channel[alpha_channel];
662       
663     if (src_alpha == IM_SAMPLE_MAX)
664       *out = *in;
665     else if (src_alpha) {
666       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
667       IM_WORK_T orig_alpha = out->channel[alpha_channel];
668       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
669         
670       for (ch = 0; ch < alpha_channel; ++ch) {
671         out->channel[ch] = ( src_alpha * in->channel[ch]
672                              + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
673                              ) / dest_alpha;
674       }
675     }
676
677     ++out;
678     ++in;
679     --count;
680   }
681 }
682
683 static void
684 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
685   if (channels == 2 || channels == 4)
686     IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
687   else
688     IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
689 }
690
691 static void
692 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
693   if (channels == 2 || channels == 4)
694     IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
695   else
696     IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
697 }
698
699 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
700 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
701 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
702 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
703 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
704 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
705 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
706 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
707 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
708 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
709 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
710 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
711
712 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
713 {
714   NULL,
715   IM_SUFFIX(combine_alphablend),
716   IM_SUFFIX(combine_mult),
717   IM_SUFFIX(combine_dissolve),
718   IM_SUFFIX(combine_add),
719   IM_SUFFIX(combine_subtract),
720   IM_SUFFIX(combine_diff),
721   IM_SUFFIX(combine_lighten),
722   IM_SUFFIX(combine_darken),
723   IM_SUFFIX(combine_hue),
724   IM_SUFFIX(combine_sat),
725   IM_SUFFIX(combine_value),
726   IM_SUFFIX(combine_color)
727 };
728
729 #/code
730
731 /*
732 =item i_get_combine(combine, color_func, fcolor_func)
733
734 =cut
735 */
736
737 void i_get_combine(int combine, i_fill_combine_f *color_func, 
738                    i_fill_combinef_f *fcolor_func) {
739   if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
740     combine = 0;
741
742   *color_func = combines_8[combine];
743   *fcolor_func = combines_double[combine];
744 }
745
746 #code
747
748 /*
749   Three good references for implementing combining modes:
750
751   http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
752   referenced as [svg1.2]
753
754   http://gimp-savvy.com/BOOK/index.html?node55.html
755   ("The Blending Modes", if it changes)
756   referenced as [savvy]
757
758   http://www.pegtop.net/delphi/articles/blendmodes/
759   referenced as [pegtop]
760
761   Where differences exist, I follow the SVG practice, the gimp
762   practice, and lastly pegtop.
763 */
764
765
766 static void 
767 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
768   IM_SUFFIX(combine_line)(out, in, channels, count);
769 }
770
771 /*
772 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
773 Da'  = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
774      = Sa + Da - Sa.Da
775
776 When Da=1
777
778 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
779  */
780 static void
781 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
782   int ch;
783   IM_COLOR *inp = in;
784   IM_COLOR *outp = out;
785   i_img_dim work_count = count;
786   int color_channels = i_color_channels(channels);
787
788   if (i_has_alpha(channels)) {
789     while (work_count--) {
790       IM_WORK_T orig_alpha = outp->channel[color_channels];
791       IM_WORK_T src_alpha = inp->channel[color_channels];
792       
793       if (src_alpha) {
794         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
795           - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
796         
797         for (ch = 0; ch < color_channels; ++ch) { 
798           outp->channel[ch] = 
799             (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
800              * orig_alpha
801              + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
802              + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
803             / IM_SAMPLE_MAX / dest_alpha;
804         }
805         outp->channel[color_channels] = dest_alpha;
806       }
807       ++outp;
808       ++inp;
809     }
810   }
811   else {
812     while (work_count--) {
813       IM_WORK_T src_alpha = inp->channel[color_channels];
814       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
815       
816       if (src_alpha) {
817         for (ch = 0; ch < color_channels; ++ch) { 
818           outp->channel[ch] = 
819             (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
820              + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
821         }
822       }
823       ++outp;
824       ++inp;
825     }
826   }
827 }
828
829 static void 
830 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
831   int color_channels = i_color_channels(channels);
832   int ch;
833
834   if (i_has_alpha(channels)) {
835     while (count--) {
836       if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
837         for (ch = 0; ch < color_channels; ++ch) {
838           out->channel[ch] = in->channel[ch];
839         }
840         out->channel[color_channels] = IM_SAMPLE_MAX;
841       }
842       ++out;
843       ++in;
844     }
845   }
846   else {
847     while (count--) {
848       if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
849         for (ch = 0; ch < color_channels; ++ch) {
850           out->channel[ch] = in->channel[ch];
851         }
852       }
853       ++out;
854       ++in;
855     }
856   }
857 }
858
859 /*
860 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
861      = Sca + Dca
862 Da'  = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
863      = Sa + Da
864 */
865
866 static void
867 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
868   int ch;
869   int color_channels = i_color_channels(channels);
870   i_img_dim work_count = count;
871   IM_COLOR *inp = in;
872   IM_COLOR *outp = out;
873
874   if (i_has_alpha(channels)) {
875     while (work_count--) {
876       IM_WORK_T src_alpha = inp->channel[color_channels];
877       if (src_alpha) {
878         IM_WORK_T orig_alpha = outp->channel[color_channels];
879         IM_WORK_T dest_alpha = src_alpha + orig_alpha;
880         if (dest_alpha > IM_SAMPLE_MAX)
881           dest_alpha = IM_SAMPLE_MAX;
882         for (ch = 0; ch < color_channels; ++ch) { 
883           IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
884           if (total > IM_SAMPLE_MAX)
885             total = IM_SAMPLE_MAX;
886           outp->channel[ch] = total;
887         }
888         outp->channel[color_channels] = dest_alpha;
889       }
890       
891       ++outp;
892       ++inp;
893     }
894   }
895   else {
896     while (work_count--) {
897       IM_WORK_T src_alpha = inp->channel[color_channels];
898       if (src_alpha) {
899         for (ch = 0; ch < color_channels; ++ch) { 
900           IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
901           if (total > IM_SAMPLE_MAX)
902             total = IM_SAMPLE_MAX;
903           outp->channel[ch] = total;
904         } 
905       }
906       
907       ++outp;
908       ++inp;
909     }
910   }
911 }
912
913 /* 
914    [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
915    it as max(A-B, 0). [svg1.2] doesn't cover it.
916
917    [savvy] doesn't document how it works with an alpha channel.  GIMP
918    actually seems to calculate the final value then use the alpha
919    channel to apply that to the target.
920  */
921 static void
922 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
923   int ch;
924   IM_COLOR const *inp = in;
925   IM_COLOR *outp = out;
926   i_img_dim work_count = count;
927   int color_channels = i_color_channels(channels);
928
929   if (i_has_alpha(channels)) {
930     while (work_count--) {
931       IM_WORK_T src_alpha = inp->channel[color_channels];
932       if (src_alpha) {
933         IM_WORK_T orig_alpha = outp->channel[color_channels];
934         IM_WORK_T dest_alpha = src_alpha + orig_alpha;
935         if (dest_alpha > IM_SAMPLE_MAX)
936           dest_alpha = IM_SAMPLE_MAX;
937         for (ch = 0; ch < color_channels; ++ch) { 
938           IM_WORK_T total = 
939             (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha) 
940             / dest_alpha;
941           if (total < 0)
942             total = 0;
943           outp->channel[ch] = total;
944         }
945         outp->channel[color_channels] = dest_alpha;
946       }
947       ++outp;
948       ++inp;
949     }
950   }
951   else {
952     while (work_count--) {
953       IM_WORK_T src_alpha = inp->channel[color_channels];
954       if (src_alpha) {
955         for (ch = 0; ch < color_channels; ++ch) { 
956           IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
957           if (total < 0)
958             total = 0;
959           outp->channel[ch] = total;
960         } 
961       }
962       ++outp;
963       ++inp;
964     }
965   }
966 }
967
968 #ifdef IM_EIGHT_BIT
969 #define IM_abs(x) abs(x)
970 #else
971 #define IM_abs(x) fabs(x)
972 #endif
973
974 /*
975 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
976      = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
977 Da'  = Sa + Da - Sa.Da 
978 */
979 static void
980 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
981   int ch;
982   IM_COLOR const *inp = in;
983   IM_COLOR *outp = out;
984   i_img_dim work_count = count;
985   int color_channels = i_color_channels(channels);
986
987   if (i_has_alpha(channels)) {
988     while (work_count--) {
989       IM_WORK_T src_alpha = inp->channel[color_channels];
990       if (src_alpha) {
991         IM_WORK_T orig_alpha = outp->channel[color_channels];
992         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
993           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
994         for (ch = 0; ch < color_channels; ++ch) {
995           IM_WORK_T src = inp->channel[ch] * src_alpha;
996           IM_WORK_T orig = outp->channel[ch] * orig_alpha;
997           IM_WORK_T src_da = src * orig_alpha;
998           IM_WORK_T dest_sa = orig * src_alpha;
999           IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1000           outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1001         }
1002         outp->channel[color_channels] = dest_alpha;
1003       }
1004       ++inp;
1005       ++outp;
1006     }
1007   }
1008   else {
1009     while (work_count--) {
1010       IM_WORK_T src_alpha = inp->channel[color_channels];
1011       if (src_alpha) {
1012         for (ch = 0; ch < color_channels; ++ch) {
1013           IM_WORK_T src = inp->channel[ch] * src_alpha;
1014           IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1015           IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1016           IM_WORK_T dest_sa = orig * src_alpha;
1017           IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1018           outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1019         }
1020       }
1021       ++inp;
1022       ++outp;
1023     }
1024   }
1025 }
1026
1027 #undef IM_abs
1028
1029 /*
1030   Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1031   Da' = Sa + Da - Sa.Da
1032
1033   To hoist some code:
1034
1035   Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1036        = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1037
1038   When Da=1:
1039
1040   Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1041        = min(Sca, Dc.Sa) + Dc(1-Sa)
1042        = Sa.min(Sc, Dc) + Dc - Dc.Sa
1043   Da'  = Sa + 1 - Sa.1
1044        = 1
1045  */
1046 static void 
1047 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels, 
1048                           i_img_dim count) {
1049   int ch;
1050   IM_COLOR const *inp = in;
1051   IM_COLOR *outp = out;
1052   i_img_dim work_count = count;
1053   int color_channels = i_color_channels(channels);
1054
1055   if (i_has_alpha(channels)) {
1056     while (work_count--) {
1057       IM_WORK_T src_alpha = inp->channel[color_channels];
1058
1059       if (src_alpha) {
1060         IM_WORK_T orig_alpha = outp->channel[color_channels];
1061         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
1062           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1063         for (ch = 0; ch < color_channels; ++ch) { 
1064           IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1065           IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1066           IM_WORK_T ScaDa = Sca * orig_alpha;
1067           IM_WORK_T DcaSa = Dca * src_alpha;
1068           IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1069           outp->channel[ch] = 
1070             ( 
1071              minc + (Sca + Dca) * IM_SAMPLE_MAX
1072              - ScaDa - DcaSa
1073              ) / (IM_SAMPLE_MAX * dest_alpha);
1074         } 
1075         outp->channel[color_channels] = dest_alpha;
1076       }
1077       ++outp;
1078       ++inp;
1079     }
1080   }
1081   else {
1082     while (work_count--) {
1083       IM_WORK_T src_alpha = inp->channel[color_channels];
1084
1085       if (src_alpha) {
1086         for (ch = 0; ch < color_channels; ++ch) { 
1087           IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1088             ? outp->channel[ch] : inp->channel[ch];
1089           outp->channel[ch] = 
1090             ( 
1091              src_alpha * minc + 
1092              outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1093              ) / IM_SAMPLE_MAX;
1094         } 
1095       }
1096       ++outp;
1097       ++inp;
1098     }
1099   }
1100 }
1101
1102 static void 
1103 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1104   int ch;
1105   IM_COLOR const *inp = in;
1106   IM_COLOR *outp = out;
1107   i_img_dim work_count = count;
1108   int color_channels = i_color_channels(channels);
1109
1110   if (i_has_alpha(channels)) {
1111     while (work_count--) {
1112       IM_WORK_T src_alpha = inp->channel[color_channels];
1113
1114       if (src_alpha) {
1115         IM_WORK_T orig_alpha = outp->channel[color_channels];
1116         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
1117           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1118         for (ch = 0; ch < color_channels; ++ch) { 
1119           IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1120           IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1121           IM_WORK_T ScaDa = Sca * orig_alpha;
1122           IM_WORK_T DcaSa = Dca * src_alpha;
1123           IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1124           outp->channel[ch] = 
1125             ( 
1126              maxc + (Sca + Dca) * IM_SAMPLE_MAX
1127              - ScaDa - DcaSa
1128              ) / (IM_SAMPLE_MAX * dest_alpha);
1129         } 
1130         outp->channel[color_channels] = dest_alpha;
1131       }
1132       ++outp;
1133       ++inp;
1134     }
1135   }
1136   else {
1137     while (work_count--) {
1138       IM_WORK_T src_alpha = inp->channel[color_channels];
1139
1140       if (src_alpha) {
1141         for (ch = 0; ch < color_channels; ++ch) { 
1142           IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1143             ? outp->channel[ch] : inp->channel[ch];
1144           outp->channel[ch] = 
1145             ( 
1146              src_alpha * maxc + 
1147              outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1148              ) / IM_SAMPLE_MAX;
1149         } 
1150       }
1151       ++outp;
1152       ++inp;
1153     }
1154   }
1155 }
1156
1157 #if IM_EIGHT_BIT
1158 #define IM_RGB_TO_HSV i_rgb_to_hsv
1159 #define IM_HSV_TO_RGB i_hsv_to_rgb
1160 #else
1161 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1162 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1163 #endif
1164
1165 static void 
1166 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1167   if (channels > 2) {
1168     IM_COLOR *inp = in;
1169     IM_COLOR const *outp = out;
1170     i_img_dim work_count = count;
1171
1172     if (i_has_alpha(channels)) {
1173       while (work_count--) {
1174         IM_COLOR c = *inp;
1175         IM_RGB_TO_HSV(&c);
1176         /* only transfer hue if there's saturation */
1177         if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1178           *inp = *outp;
1179           IM_RGB_TO_HSV(inp);
1180           /* and no point in setting the target hue if the target has no sat */
1181           if (inp->channel[1]) {
1182             inp->channel[0] = c.channel[0];
1183             IM_HSV_TO_RGB(inp);
1184             inp->channel[3] = c.channel[3];
1185           }
1186           else {
1187             inp->channel[3] = 0;
1188           }
1189         }
1190         else {
1191           inp->channel[3] = 0;
1192         }
1193         
1194         ++outp;
1195         ++inp;
1196       }
1197     }
1198     else {
1199       while (work_count--) {
1200         IM_COLOR c = *inp;
1201         IM_RGB_TO_HSV(&c);
1202         /* only transfer hue if there's saturation */
1203         if (c.channel[1] && inp->channel[3]) {
1204           *inp = *outp;
1205           IM_RGB_TO_HSV(inp);
1206           /* and no point in setting the target hue if the target has no sat */
1207           if (inp->channel[1]) {
1208             inp->channel[0] = c.channel[0];
1209             IM_HSV_TO_RGB(inp);
1210             inp->channel[3] = c.channel[3];
1211           }
1212         }
1213         else {
1214           inp->channel[3] = 0;
1215         }
1216         
1217         ++outp;
1218         ++inp;
1219       }
1220     }
1221
1222     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1223   }
1224 }
1225
1226 static void
1227 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1228   if (channels > 2) {
1229     IM_COLOR *inp = in;
1230     IM_COLOR const *outp = out;
1231     i_img_dim work_count = count;
1232
1233     while (work_count--) {
1234       IM_COLOR c = *inp;
1235       *inp = *outp;
1236       IM_RGB_TO_HSV(&c);
1237       IM_RGB_TO_HSV(inp);
1238       inp->channel[1] = c.channel[1];
1239       IM_HSV_TO_RGB(inp);
1240       inp->channel[3] = c.channel[3];
1241       ++outp;
1242       ++inp;
1243     }
1244
1245     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1246   }
1247 }
1248
1249 static void 
1250 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1251   if (channels > 2) {
1252     IM_COLOR *inp = in;
1253     IM_COLOR const *outp = out;
1254     i_img_dim work_count = count;
1255
1256     while (work_count--) {
1257       IM_COLOR c = *inp;
1258       *inp = *outp;
1259       IM_RGB_TO_HSV(&c);
1260       IM_RGB_TO_HSV(inp);
1261       inp->channel[2] = c.channel[2];
1262       IM_HSV_TO_RGB(inp);
1263       inp->channel[3] = c.channel[3];
1264       ++outp;
1265       ++inp;
1266     }
1267   }
1268
1269   /* all images have a "value channel" - for greyscale it's the only
1270      colour channel */
1271   IM_SUFFIX(combine_line_na)(out, in, channels, count);
1272 }
1273
1274 static void 
1275 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1276   if (channels > 2) {
1277     IM_COLOR *inp = in;
1278     IM_COLOR const *outp = out;
1279     i_img_dim work_count = count;
1280
1281     while (work_count--) {
1282       IM_COLOR c = *inp;
1283       *inp = *outp;
1284       IM_RGB_TO_HSV(&c);
1285       IM_RGB_TO_HSV(inp);
1286       inp->channel[0] = c.channel[0];
1287       inp->channel[1] = c.channel[1];
1288       IM_HSV_TO_RGB(inp);
1289       inp->channel[3] = c.channel[3];
1290       ++outp;
1291       ++inp;
1292     }
1293
1294     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1295   }
1296 }
1297
1298 #undef IM_RGB_TO_HSV
1299 #undef IM_HSV_TO_RGB
1300
1301 #/code