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