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