commit changes from draw branch
[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 #if 0
193   /* make sure our line buffer is big enough */
194   if (width > r->width) {
195     int new_width = r->width * 2;
196     if (new_width < width)
197       new_width = width;
198
199     if (r->im->bits <= 8) {
200       if (r->line_8)
201         r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
202       else
203         r->line_8 = mymalloc(sizeof(i_color) * new_width);
204       if (r->line_double) {
205         myfree(r->line_double);
206         r->line_double = NULL;
207       }
208     }
209     else {
210       if (r->line_double)
211         r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
212       else
213         r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
214       if (r->line_8) {
215         myfree(r->line_8);
216         r->line_double = NULL;
217       }
218     }
219
220     r->width = new_width;
221   }
222 #else
223   alloc_line(r, width, r->im->bits <= 8);
224 #endif
225
226 #code r->im->bits <= 8
227   /*if (r->IM_SUFFIX(line) == NULL)
228     r->IM_SUFFIX(line) = mymalloc(sizeof(IM_COLOR) * r->width);*/
229   (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
230 #/code
231 }
232
233 void
234 i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
235               i_fill_t *fill) {
236   i_img *im = r->im;
237   int fill_channels = im->channels;
238   
239   if (fill_channels == 1 || fill_channels == 3)
240     ++fill_channels;
241
242   if (y < 0 || y >= im->ysize)
243     return;
244   if (x < 0) {
245     width += x;
246     src -= x;
247     x = 0;
248   }
249   if (x + width > im->xsize) {
250     width = im->xsize - x;
251   }
252   if (x >= im->xsize || x + width <= 0 || width <= 0)
253     return;
254
255   if (src) {
256     /* avoid as much work as we can */
257     while (width > 0 && *src == 0) {
258       --width;
259       ++src;
260       ++x;
261     }
262     while (width > 0 && src[width-1] == 0) {
263       --width;
264     }
265   }
266   if (!width)
267     return;
268
269 #if 0
270   if (r->im->bits <= 8 && fill->f_fill_with_color) {
271     if (!r->line_8)
272       r->line_8 = mymalloc(sizeof(i_color) * r->width);
273     if (!r->fill_line_8)
274       r->fill_line_8 = mymalloc(sizeof(i_color) * r->width);
275   }
276   else {
277     if (!r->line_double)
278       r->line_double = mymalloc(sizeof(i_fcolor) * r->width);
279     if (!r->fill_line_double)
280       r->fill_line_double = mymalloc(sizeof(i_fcolor) * r->width);
281   }
282
283   /* make sure our line buffer is big enough */
284   if (width > r->width) {
285     int new_width = r->width * 2;
286     if (new_width < width)
287       new_width = width;
288
289     if (r->im->bits <= 8 && fill->f_fill_with_color) {
290       r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
291       r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
292       if (r->line_double) {
293         myfree(r->line_double);
294         r->line_double = NULL;
295       }
296       if (r->fill_line_double) {
297         myfree(r->fill_line_double);
298         r->fill_line_double = NULL;
299       }
300     }
301     else {
302       r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
303       r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
304       if (r->line_8) {
305         myfree(r->line_8);
306         r->line_8 = NULL;
307       }
308       if (r->fill_line_8) {
309         myfree(r->fill_line_8);
310         r->fill_line_8 = NULL;
311       }
312     }
313     r->width = new_width;
314   }
315 #else
316   alloc_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
317   alloc_fill_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
318 #endif
319
320 #code r->im->bits <= 8 && fill->f_fill_with_color
321   if (IM_FILL_COMBINE(fill)) {
322     IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
323     IM_COLOR *destc = r->IM_SUFFIX(line);
324     IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
325     if (src) {
326       unsigned char const *srcc = src;
327       IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
328       int work_width = width;
329       while (work_width) {
330         if (*srcc == 0) {
331           fillc->channel[fill_channels-1] = 0;
332         }
333         else if (*srcc != 255) {
334           fillc->channel[fill_channels-1] =
335             fillc->channel[fill_channels-1] * *srcc / 255;
336         }
337         --work_width;
338         ++srcc;
339         ++fillc;
340       }
341     }
342     IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
343     IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
344   }
345   else {
346     if (src) {
347       int work_width = width;
348       IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
349       IM_COLOR *destc = r->IM_SUFFIX(line);
350       int ch;
351
352       IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
353       IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
354       while (work_width) {
355         if (*src == 255) {
356           /* just replace it */
357           *destc = *srcc;
358         }
359         else if (*src) {
360           for (ch = 0; ch < im->channels; ++ch) {
361             IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
362                               + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
363             destc->channel[ch] = IM_LIMIT(work);
364           }
365         }
366         
367         ++srcc;
368         ++destc;
369         ++src;
370         --work_width;
371       }
372     }
373     else { /* if (src) */
374       IM_FILL_FILLER(fill)(fill, x, y, width, r->im->channels, r->IM_SUFFIX(line));
375     }
376   }
377   IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
378 #/code
379 }
380
381 static void
382 dump_src(const char *note, unsigned char const *src, int width) {
383   int i;
384   printf("%s - %p/%d\n", note, src, width);
385   for (i = 0; i < width; ++i) {
386     printf("%02x ", src[i]);
387   }
388   putchar('\n');
389 }
390
391 #code
392
393 void
394 IM_RENDER_LINE(i_render *r, int x, int y, int width, const IM_SAMPLE_T *src,
395               IM_COLOR *line, 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       int 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       int 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, int x, int y, int width, 
482                 unsigned char const *src, i_color const *color) {
483   i_img *im = r->im;
484   IM_COLOR *linep = r->IM_SUFFIX(line);
485   int ch, channels = im->channels;
486   int fetch_offset;
487 #undef STORE_COLOR
488 #ifdef IM_EIGHT_BIT
489 #define STORE_COLOR (*color)
490 #else
491   i_fcolor fcolor;
492
493   for (ch = 0; ch < channels; ++ch) {
494     fcolor.channel[ch] = color->channel[ch] / 255.0;
495   }
496 #define STORE_COLOR fcolor
497 #endif
498  
499   fetch_offset = 0;
500   while (fetch_offset < width && *src == 0xFF) {
501     *linep++ = STORE_COLOR;
502     ++src;
503     ++fetch_offset;
504   }
505   IM_GLIN(im, x+fetch_offset, x+width, y, linep);
506   while (fetch_offset < width) {
507 #ifdef IM_EIGHT_BIT
508     IM_WORK_T alpha = *src++;
509 #else
510     IM_WORK_T alpha = *src++ / 255.0;
511 #endif
512     if (alpha == IM_SAMPLE_MAX)
513       *linep = STORE_COLOR;
514     else if (alpha) {
515       for (ch = 0; ch < channels; ++ch) {
516         linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha) 
517                               + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
518       }
519     }
520     ++linep;
521     ++fetch_offset;
522   }
523   IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
524 }
525
526 static
527 void
528 IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width, 
529                 unsigned char const *src, i_color const *color) {
530   IM_COLOR *linep = r->IM_SUFFIX(line);
531   int ch;
532   int alpha_channel = r->im->channels - 1;
533   int fetch_offset;
534 #undef STORE_COLOR
535 #ifdef IM_EIGHT_BIT
536 #define STORE_COLOR (*color)
537 #else
538   i_fcolor fcolor;
539
540   for (ch = 0; ch < r->im->channels; ++ch) {
541     fcolor.channel[ch] = color->channel[ch] / 255.0;
542   }
543 #define STORE_COLOR fcolor
544 #endif
545
546   fetch_offset = 0;
547   while (fetch_offset < width && *src == 0xFF) {
548     *linep++ = STORE_COLOR;
549     ++src;
550     ++fetch_offset;
551   }
552   IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
553   while (fetch_offset < width) {
554 #ifdef IM_EIGHT_BIT
555     IM_WORK_T src_alpha = *src++;
556 #else
557     IM_WORK_T src_alpha = *src++ / 255.0;
558 #endif
559     if (src_alpha == IM_SAMPLE_MAX)
560       *linep = STORE_COLOR;
561     else if (src_alpha) {
562       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
563       IM_WORK_T orig_alpha = linep->channel[alpha_channel];
564       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
565       for (ch = 0; ch < alpha_channel; ++ch) {
566         linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
567                                + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
568                                ) / dest_alpha;
569       }
570       linep->channel[alpha_channel] = dest_alpha;
571     }
572     ++linep;
573     ++fetch_offset;
574   }
575   IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
576 #undef STORE_COLOR
577 }
578
579 /* combine a line of image data with an output line, both the input
580    and output lines include an alpha channel.
581
582    Both input and output lines have I<channels> of data, channels
583    should be either 2 or 4.
584 */
585
586 static void
587 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, 
588                               int channels, int count) {
589   int ch;
590   int alpha_channel = channels - 1;
591   
592   while (count) {
593     IM_WORK_T src_alpha = in->channel[alpha_channel];
594       
595     if (src_alpha == IM_SAMPLE_MAX)
596       *out = *in;
597     else if (src_alpha) {
598       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
599       IM_WORK_T orig_alpha = out->channel[alpha_channel];
600       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
601         
602       for (ch = 0; ch < alpha_channel; ++ch) {
603         out->channel[ch] = ( src_alpha * in->channel[ch]
604                              + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
605                              ) / dest_alpha;
606       }
607       out->channel[alpha_channel] = dest_alpha;
608     }
609
610     ++out;
611     ++in;
612     --count;
613   }
614 }
615
616 /* combine a line of image data with an output line.  The input line
617    includes an alpha channel, the output line has no alpha channel.
618    
619    The input line has I<channels>+1 of color data.  The output line
620    has I<channels> of color data.
621 */
622
623 static void
624 IM_SUFFIX(combine_line_noalpha)
625      (IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
626   int ch;
627
628   while (count) {
629     IM_WORK_T src_alpha = in->channel[channels];
630     
631     if (src_alpha == IM_SAMPLE_MAX)
632       *out = *in;
633     else if (src_alpha) {
634       IM_WORK_T remains;
635       
636       remains = IM_SAMPLE_MAX - src_alpha;
637       for (ch = 0; ch < channels; ++ch) {
638         out->channel[ch] = ( in->channel[ch] * src_alpha
639                              + out->channel[ch] * remains) / IM_SAMPLE_MAX;
640       }
641     }
642     
643     ++out;
644     ++in;
645     --count;
646   }
647 }
648
649 /* combine a line of image data with an output line, both the input
650    and output lines include an alpha channel.
651
652    Both input and output lines have I<channels> of data, channels
653    should be either 2 or 4.
654
655    This variant does not modify the output alpha channel.
656 */
657
658 static void
659 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, 
660                                    int channels, int count) {
661   int ch;
662   int alpha_channel = channels - 1;
663   
664   while (count) {
665     IM_WORK_T src_alpha = in->channel[alpha_channel];
666       
667     if (src_alpha == IM_SAMPLE_MAX)
668       *out = *in;
669     else if (src_alpha) {
670       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
671       IM_WORK_T orig_alpha = out->channel[alpha_channel];
672       IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
673         
674       for (ch = 0; ch < alpha_channel; ++ch) {
675         out->channel[ch] = ( src_alpha * in->channel[ch]
676                              + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
677                              ) / dest_alpha;
678       }
679     }
680
681     ++out;
682     ++in;
683     --count;
684   }
685 }
686
687 static void
688 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
689   if (channels == 2 || channels == 4)
690     IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
691   else
692     IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
693 }
694
695 static void
696 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
697   if (channels == 2 || channels == 4)
698     IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
699   else
700     IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
701 }
702
703 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, int);
704 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, int);
705 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, int);
706 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, int);
707 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, int);
708 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, int);
709 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, int);
710 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, int);
711 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, int);
712 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, int);
713 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, int);
714 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, int);
715
716 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
717 {
718   NULL,
719   IM_SUFFIX(combine_alphablend),
720   IM_SUFFIX(combine_mult),
721   IM_SUFFIX(combine_dissolve),
722   IM_SUFFIX(combine_add),
723   IM_SUFFIX(combine_subtract),
724   IM_SUFFIX(combine_diff),
725   IM_SUFFIX(combine_lighten),
726   IM_SUFFIX(combine_darken),
727   IM_SUFFIX(combine_hue),
728   IM_SUFFIX(combine_sat),
729   IM_SUFFIX(combine_value),
730   IM_SUFFIX(combine_color)
731 };
732
733 #/code
734
735 /*
736 =item i_get_combine(combine, color_func, fcolor_func)
737
738 =cut
739 */
740
741 void i_get_combine(int combine, i_fill_combine_f *color_func, 
742                    i_fill_combinef_f *fcolor_func) {
743   if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
744     combine = 0;
745
746   *color_func = combines_8[combine];
747   *fcolor_func = combines_double[combine];
748 }
749
750 #code
751
752 /*
753   Three good references for implementing combining modes:
754
755   http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
756   referenced as [svg1.2]
757
758   http://gimp-savvy.com/BOOK/index.html?node55.html
759   ("The Blending Modes", if it changes)
760   referenced as [savvy]
761
762   http://www.pegtop.net/delphi/articles/blendmodes/
763   referenced as [pegtop]
764
765   Where differences exist, I follow the SVG practice, the gimp
766   practice, and lastly pegtop.
767 */
768
769
770 static void 
771 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
772   IM_SUFFIX(combine_line)(out, in, channels, count);
773 }
774
775 /*
776 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
777 Da'  = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
778      = Sa + Da - Sa.Da
779
780 When Da=1
781
782 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
783  */
784 static void
785 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
786   int ch;
787   IM_COLOR *inp = in;
788   IM_COLOR *outp = out;
789   int work_count = count;
790   int color_channels = i_color_channels(channels);
791
792   if (i_has_alpha(channels)) {
793     while (work_count--) {
794       IM_WORK_T orig_alpha = outp->channel[color_channels];
795       IM_WORK_T src_alpha = inp->channel[color_channels];
796       
797       if (src_alpha) {
798         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
799           - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
800         
801         for (ch = 0; ch < color_channels; ++ch) { 
802           outp->channel[ch] = 
803             (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
804              * orig_alpha
805              + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
806              + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
807             / IM_SAMPLE_MAX / dest_alpha;
808         }
809         outp->channel[color_channels] = dest_alpha;
810       }
811       ++outp;
812       ++inp;
813     }
814   }
815   else {
816     while (work_count--) {
817       IM_WORK_T src_alpha = inp->channel[color_channels];
818       IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
819       
820       if (src_alpha) {
821         for (ch = 0; ch < color_channels; ++ch) { 
822           outp->channel[ch] = 
823             (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
824              + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
825         }
826       }
827       ++outp;
828       ++inp;
829     }
830   }
831 }
832
833 static void 
834 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
835   int color_channels = i_color_channels(channels);
836   int ch;
837
838   if (i_has_alpha(channels)) {
839     while (count--) {
840       if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
841         for (ch = 0; ch < color_channels; ++ch) {
842           out->channel[ch] = in->channel[ch];
843         }
844         out->channel[color_channels] = IM_SAMPLE_MAX;
845       }
846       ++out;
847       ++in;
848     }
849   }
850   else {
851     while (count--) {
852       if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
853         for (ch = 0; ch < color_channels; ++ch) {
854           out->channel[ch] = in->channel[ch];
855         }
856       }
857       ++out;
858       ++in;
859     }
860   }
861 }
862
863 /*
864 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
865      = Sca + Dca
866 Da'  = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
867      = Sa + Da
868 */
869
870 static void
871 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
872   int ch;
873   int color_channels = i_color_channels(channels);
874   int work_count = count;
875   IM_COLOR *inp = in;
876   IM_COLOR *outp = out;
877
878   if (i_has_alpha(channels)) {
879     while (work_count--) {
880       IM_WORK_T src_alpha = inp->channel[color_channels];
881       if (src_alpha) {
882         IM_WORK_T orig_alpha = outp->channel[color_channels];
883         IM_WORK_T dest_alpha = src_alpha + orig_alpha;
884         if (dest_alpha > IM_SAMPLE_MAX)
885           dest_alpha = IM_SAMPLE_MAX;
886         for (ch = 0; ch < color_channels; ++ch) { 
887           IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
888           if (total > IM_SAMPLE_MAX)
889             total = IM_SAMPLE_MAX;
890           outp->channel[ch] = total;
891         }
892         outp->channel[color_channels] = dest_alpha;
893       }
894       
895       ++outp;
896       ++inp;
897     }
898   }
899   else {
900     while (work_count--) {
901       IM_WORK_T src_alpha = inp->channel[color_channels];
902       if (src_alpha) {
903         for (ch = 0; ch < color_channels; ++ch) { 
904           IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
905           if (total > IM_SAMPLE_MAX)
906             total = IM_SAMPLE_MAX;
907           outp->channel[ch] = total;
908         } 
909       }
910       
911       ++outp;
912       ++inp;
913     }
914   }
915 }
916
917 /* 
918    [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
919    it as max(A-B, 0). [svg1.2] doesn't cover it.
920
921    [savvy] doesn't document how it works with an alpha channel.  GIMP
922    actually seems to calculate the final value then use the alpha
923    channel to apply that to the target.
924  */
925 static void
926 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
927   int ch;
928   IM_COLOR const *inp = in;
929   IM_COLOR *outp = out;
930   int work_count = count;
931   int color_channels = i_color_channels(channels);
932
933   if (i_has_alpha(channels)) {
934     while (work_count--) {
935       IM_WORK_T src_alpha = inp->channel[color_channels];
936       if (src_alpha) {
937         IM_WORK_T orig_alpha = outp->channel[color_channels];
938         IM_WORK_T dest_alpha = src_alpha + orig_alpha;
939         if (dest_alpha > IM_SAMPLE_MAX)
940           dest_alpha = IM_SAMPLE_MAX;
941         for (ch = 0; ch < color_channels; ++ch) { 
942           IM_WORK_T total = 
943             (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha) 
944             / dest_alpha;
945           if (total < 0)
946             total = 0;
947           outp->channel[ch] = total;
948         }
949         outp->channel[color_channels] = dest_alpha;
950       }
951       ++outp;
952       ++inp;
953     }
954   }
955   else {
956     while (work_count--) {
957       IM_WORK_T src_alpha = inp->channel[color_channels];
958       if (src_alpha) {
959         for (ch = 0; ch < color_channels; ++ch) { 
960           IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
961           if (total < 0)
962             total = 0;
963           outp->channel[ch] = total;
964         } 
965       }
966       ++outp;
967       ++inp;
968     }
969   }
970 }
971
972 #ifdef IM_EIGHT_BIT
973 #define IM_abs(x) abs(x)
974 #else
975 #define IM_abs(x) fabs(x)
976 #endif
977
978 /*
979 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
980      = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
981 Da'  = Sa + Da - Sa.Da 
982 */
983 static void
984 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
985   int ch;
986   IM_COLOR const *inp = in;
987   IM_COLOR *outp = out;
988   int work_count = count;
989   int color_channels = i_color_channels(channels);
990
991   if (i_has_alpha(channels)) {
992     while (work_count--) {
993       IM_WORK_T src_alpha = inp->channel[color_channels];
994       if (src_alpha) {
995         IM_WORK_T orig_alpha = outp->channel[color_channels];
996         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
997           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
998         for (ch = 0; ch < color_channels; ++ch) {
999           IM_WORK_T src = inp->channel[ch] * src_alpha;
1000           IM_WORK_T orig = outp->channel[ch] * orig_alpha;
1001           IM_WORK_T src_da = src * orig_alpha;
1002           IM_WORK_T dest_sa = orig * src_alpha;
1003           IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1004           outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1005         }
1006         outp->channel[color_channels] = dest_alpha;
1007       }
1008       ++inp;
1009       ++outp;
1010     }
1011   }
1012   else {
1013     while (work_count--) {
1014       IM_WORK_T src_alpha = inp->channel[color_channels];
1015       if (src_alpha) {
1016         for (ch = 0; ch < color_channels; ++ch) {
1017           IM_WORK_T src = inp->channel[ch] * src_alpha;
1018           IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1019           IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1020           IM_WORK_T dest_sa = orig * src_alpha;
1021           IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1022           outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1023         }
1024       }
1025       ++inp;
1026       ++outp;
1027     }
1028   }
1029 }
1030
1031 #undef IM_abs
1032
1033 /*
1034   Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1035   Da' = Sa + Da - Sa.Da
1036
1037   To hoist some code:
1038
1039   Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1040        = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1041
1042   When Da=1:
1043
1044   Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1045        = min(Sca, Dc.Sa) + Dc(1-Sa)
1046        = Sa.min(Sc, Dc) + Dc - Dc.Sa
1047   Da'  = Sa + 1 - Sa.1
1048        = 1
1049  */
1050 static void 
1051 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels, 
1052                           int count) {
1053   int ch;
1054   IM_COLOR const *inp = in;
1055   IM_COLOR *outp = out;
1056   int work_count = count;
1057   int color_channels = i_color_channels(channels);
1058
1059   if (i_has_alpha(channels)) {
1060     while (work_count--) {
1061       IM_WORK_T src_alpha = inp->channel[color_channels];
1062
1063       if (src_alpha) {
1064         IM_WORK_T orig_alpha = outp->channel[color_channels];
1065         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
1066           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1067         for (ch = 0; ch < color_channels; ++ch) { 
1068           IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1069           IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1070           IM_WORK_T ScaDa = Sca * orig_alpha;
1071           IM_WORK_T DcaSa = Dca * src_alpha;
1072           IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1073           outp->channel[ch] = 
1074             ( 
1075              minc + (Sca + Dca) * IM_SAMPLE_MAX
1076              - ScaDa - DcaSa
1077              ) / (IM_SAMPLE_MAX * dest_alpha);
1078         } 
1079         outp->channel[color_channels] = dest_alpha;
1080       }
1081       ++outp;
1082       ++inp;
1083     }
1084   }
1085   else {
1086     while (work_count--) {
1087       IM_WORK_T src_alpha = inp->channel[color_channels];
1088
1089       if (src_alpha) {
1090         for (ch = 0; ch < color_channels; ++ch) { 
1091           IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1092             ? outp->channel[ch] : inp->channel[ch];
1093           outp->channel[ch] = 
1094             ( 
1095              src_alpha * minc + 
1096              outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1097              ) / IM_SAMPLE_MAX;
1098         } 
1099       }
1100       ++outp;
1101       ++inp;
1102     }
1103   }
1104 }
1105
1106 static void 
1107 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1108   int ch;
1109   IM_COLOR const *inp = in;
1110   IM_COLOR *outp = out;
1111   int work_count = count;
1112   int color_channels = i_color_channels(channels);
1113
1114   if (i_has_alpha(channels)) {
1115     while (work_count--) {
1116       IM_WORK_T src_alpha = inp->channel[color_channels];
1117
1118       if (src_alpha) {
1119         IM_WORK_T orig_alpha = outp->channel[color_channels];
1120         IM_WORK_T dest_alpha = src_alpha + orig_alpha 
1121           - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1122         for (ch = 0; ch < color_channels; ++ch) { 
1123           IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1124           IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1125           IM_WORK_T ScaDa = Sca * orig_alpha;
1126           IM_WORK_T DcaSa = Dca * src_alpha;
1127           IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1128           outp->channel[ch] = 
1129             ( 
1130              maxc + (Sca + Dca) * IM_SAMPLE_MAX
1131              - ScaDa - DcaSa
1132              ) / (IM_SAMPLE_MAX * dest_alpha);
1133         } 
1134         outp->channel[color_channels] = dest_alpha;
1135       }
1136       ++outp;
1137       ++inp;
1138     }
1139   }
1140   else {
1141     while (work_count--) {
1142       IM_WORK_T src_alpha = inp->channel[color_channels];
1143
1144       if (src_alpha) {
1145         for (ch = 0; ch < color_channels; ++ch) { 
1146           IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1147             ? outp->channel[ch] : inp->channel[ch];
1148           outp->channel[ch] = 
1149             ( 
1150              src_alpha * maxc + 
1151              outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1152              ) / IM_SAMPLE_MAX;
1153         } 
1154       }
1155       ++outp;
1156       ++inp;
1157     }
1158   }
1159 }
1160
1161 #if IM_EIGHT_BIT
1162 #define IM_RGB_TO_HSV i_rgb_to_hsv
1163 #define IM_HSV_TO_RGB i_hsv_to_rgb
1164 #else
1165 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1166 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1167 #endif
1168
1169 static void 
1170 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1171   if (channels > 2) {
1172     IM_COLOR *inp = in;
1173     IM_COLOR const *outp = out;
1174     int work_count = count;
1175
1176     if (i_has_alpha(channels)) {
1177       while (work_count--) {
1178         IM_COLOR c = *inp;
1179         IM_RGB_TO_HSV(&c);
1180         /* only transfer hue if there's saturation */
1181         if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1182           *inp = *outp;
1183           IM_RGB_TO_HSV(inp);
1184           /* and no point in setting the target hue if the target has no sat */
1185           if (inp->channel[1]) {
1186             inp->channel[0] = c.channel[0];
1187             IM_HSV_TO_RGB(inp);
1188             inp->channel[3] = c.channel[3];
1189           }
1190           else {
1191             inp->channel[3] = 0;
1192           }
1193         }
1194         else {
1195           inp->channel[3] = 0;
1196         }
1197         
1198         ++outp;
1199         ++inp;
1200       }
1201     }
1202     else {
1203       while (work_count--) {
1204         IM_COLOR c = *inp;
1205         IM_RGB_TO_HSV(&c);
1206         /* only transfer hue if there's saturation */
1207         if (c.channel[1] && inp->channel[3]) {
1208           *inp = *outp;
1209           IM_RGB_TO_HSV(inp);
1210           /* and no point in setting the target hue if the target has no sat */
1211           if (inp->channel[1]) {
1212             inp->channel[0] = c.channel[0];
1213             IM_HSV_TO_RGB(inp);
1214             inp->channel[3] = c.channel[3];
1215           }
1216         }
1217         else {
1218           inp->channel[3] = 0;
1219         }
1220         
1221         ++outp;
1222         ++inp;
1223       }
1224     }
1225
1226     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1227   }
1228 }
1229
1230 static void
1231 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1232   if (channels > 2) {
1233     IM_COLOR *inp = in;
1234     IM_COLOR const *outp = out;
1235     int work_count = count;
1236
1237     while (work_count--) {
1238       IM_COLOR c = *inp;
1239       *inp = *outp;
1240       IM_RGB_TO_HSV(&c);
1241       IM_RGB_TO_HSV(inp);
1242       inp->channel[1] = c.channel[1];
1243       IM_HSV_TO_RGB(inp);
1244       inp->channel[3] = c.channel[3];
1245       ++outp;
1246       ++inp;
1247     }
1248
1249     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1250   }
1251 }
1252
1253 static void 
1254 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1255   if (channels > 2) {
1256     IM_COLOR *inp = in;
1257     IM_COLOR const *outp = out;
1258     int work_count = count;
1259
1260     while (work_count--) {
1261       IM_COLOR c = *inp;
1262       *inp = *outp;
1263       IM_RGB_TO_HSV(&c);
1264       IM_RGB_TO_HSV(inp);
1265       inp->channel[2] = c.channel[2];
1266       IM_HSV_TO_RGB(inp);
1267       inp->channel[3] = c.channel[3];
1268       ++outp;
1269       ++inp;
1270     }
1271   }
1272
1273   /* all images have a "value channel" - for greyscale it's the only
1274      colour channel */
1275   IM_SUFFIX(combine_line_na)(out, in, channels, count);
1276 }
1277
1278 static void 
1279 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1280   if (channels > 2) {
1281     IM_COLOR *inp = in;
1282     IM_COLOR const *outp = out;
1283     int work_count = count;
1284
1285     while (work_count--) {
1286       IM_COLOR c = *inp;
1287       *inp = *outp;
1288       IM_RGB_TO_HSV(&c);
1289       IM_RGB_TO_HSV(inp);
1290       inp->channel[0] = c.channel[0];
1291       inp->channel[1] = c.channel[1];
1292       IM_HSV_TO_RGB(inp);
1293       inp->channel[3] = c.channel[3];
1294       ++outp;
1295       ++inp;
1296     }
1297
1298     IM_SUFFIX(combine_line_na)(out, in, channels, count);
1299   }
1300 }
1301
1302 #undef IM_RGB_TO_HSV
1303 #undef IM_HSV_TO_RGB
1304
1305 #/code