6 #define RENDER_MAGIC 0x765AE
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);
10 #define i_has_alpha(channels) ((channels) == 2 || (channels) == 4)
12 #define i_color_channels(channels) (i_has_alpha(channels) ? (channels)-1 : (channels))
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);
19 static render_color_f IM_SUFFIX(render_color_tab)[] =
22 IM_SUFFIX(render_color_13),
23 IM_SUFFIX(render_color_alpha),
24 IM_SUFFIX(render_color_13),
25 IM_SUFFIX(render_color_alpha),
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);
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);
39 =item i_render_new(im, width)
42 Allocate a new C<i_render> object and initialize it.
48 i_render_new(i_img *im, i_img_dim width) {
49 i_render *r = mymalloc(sizeof(i_render));
51 i_render_init(r, im, width);
57 =item i_render_delete(r)
60 Release an C<i_render> object.
66 i_render_delete(i_render *r) {
72 i_render_init(i_render *r, i_img *im, i_img_dim width) {
73 r->magic = RENDER_MAGIC;
75 r->line_width = width;
77 r->line_double = NULL;
78 r->fill_width = width;
79 r->fill_line_8 = NULL;
80 r->fill_line_double = NULL;
84 i_render_done(i_render *r) {
88 myfree(r->line_double);
90 myfree(r->fill_line_8);
91 if (r->fill_line_double)
92 myfree(r->fill_line_double);
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)
105 r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
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;
115 r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
117 r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
120 r->line_double = NULL;
124 r->line_width = new_width;
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;
137 r->line_double = mymalloc(sizeof(i_fcolor) * r->line_width);
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)
155 r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
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;
164 if (r->fill_line_double)
165 r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
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;
174 r->fill_width = new_width;
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;
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;
197 =item i_render_color(r, x, y, width, source, color)
200 Render the given color with the coverage specified by C<source[0]> to
203 Renders in normal combine mode.
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) {
212 if (y < 0 || y >= im->ysize)
219 if (x + width > im->xsize) {
220 width = im->xsize - x;
222 if (x >= im->xsize || x + width <= 0 || width <= 0)
225 /* avoid as much work as we can */
226 while (width > 0 && *src == 0) {
231 while (width > 0 && src[width-1] == 0) {
237 alloc_line(r, width, r->im->bits <= 8);
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);
247 =item i_render_fill(r, x, y, width, source, fill)
250 Render the given fill with the coverage in C<source[0]> through
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) {
260 int fill_channels = im->channels;
262 if (fill_channels == 1 || fill_channels == 3)
265 if (y < 0 || y >= im->ysize)
272 if (x + width > im->xsize) {
273 width = im->xsize - x;
275 if (x >= im->xsize || x + width <= 0 || width <= 0)
279 /* avoid as much work as we can */
280 while (width > 0 && *src == 0) {
285 while (width > 0 && src[width-1] == 0) {
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);
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));
301 unsigned char const *srcc = src;
302 IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
303 i_img_dim work_width = width;
306 fillc->channel[fill_channels-1] = 0;
308 else if (*srcc != 255) {
309 fillc->channel[fill_channels-1] =
310 fillc->channel[fill_channels-1] * *srcc / 255;
317 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
318 IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
322 i_img_dim work_width = width;
323 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
324 IM_COLOR *destc = r->IM_SUFFIX(line);
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));
331 /* just replace it */
335 for (ch = 0; ch < im->channels; ++ch) {
336 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
337 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
338 destc->channel[ch] = IM_LIMIT(work);
348 else { /* if (src) */
349 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(line));
352 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
361 dump_src(const char *note, unsigned char const *src, i_img_dim width) {
363 printf("%s - %p/%" i_DF "\n", note, src, i_DFc(width));
364 for (i = 0; i < width; ++i) {
365 printf("%02x ", src[i]);
375 =item i_render_line(r, x, y, width, source, fill)
378 Render the given fill with the coverage in C<source[0]> through
383 =item i_render_linef(r, x, y, width, source, fill)
386 Render the given fill with the coverage in C<source[0]> through
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) {
397 int src_chans = im->channels;
399 /* src must always have an alpha channel */
400 if (src_chans == 1 || src_chans == 3)
403 if (y < 0 || y >= im->ysize)
411 if (x + width > im->xsize)
412 width = r->im->xsize - x;
415 alloc_line(r, width, 1);
417 alloc_line(r, width, 0);
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;
429 if (*srcp != IM_SAMPLE_MAX)
430 linep->channel[alpha_chan] =
431 linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
434 linep->channel[alpha_chan] = 0;
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));
447 i_img_dim work_width = width;
448 IM_COLOR *srcc = line;
449 IM_COLOR *destc = r->IM_SUFFIX(line);
451 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
454 /* just replace it */
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);
471 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
474 IM_PLIN(im, x, x+width, y, line);
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) {
485 IM_COLOR *linep = r->IM_SUFFIX(line);
486 int ch, channels = im->channels;
487 i_img_dim fetch_offset;
490 #define STORE_COLOR (*color)
494 for (ch = 0; ch < channels; ++ch) {
495 fcolor.channel[ch] = color->channel[ch] / 255.0;
497 #define STORE_COLOR fcolor
501 while (fetch_offset < width && *src == 0xFF) {
502 *linep++ = STORE_COLOR;
506 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
507 while (fetch_offset < width) {
509 IM_WORK_T alpha = *src++;
511 IM_WORK_T alpha = *src++ / 255.0;
513 if (alpha == IM_SAMPLE_MAX)
514 *linep = STORE_COLOR;
516 for (ch = 0; ch < channels; ++ch) {
517 linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
518 + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
524 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
529 IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
530 i_img_dim width, unsigned char const *src,
531 i_color const *color) {
532 IM_COLOR *linep = r->IM_SUFFIX(line);
534 int alpha_channel = r->im->channels - 1;
535 i_img_dim fetch_offset;
538 #define STORE_COLOR (*color)
542 for (ch = 0; ch < r->im->channels; ++ch) {
543 fcolor.channel[ch] = color->channel[ch] / 255.0;
545 #define STORE_COLOR fcolor
549 while (fetch_offset < width && *src == 0xFF) {
550 *linep++ = STORE_COLOR;
554 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
555 while (fetch_offset < width) {
557 IM_WORK_T src_alpha = *src++;
559 IM_WORK_T src_alpha = *src++ / 255.0;
561 if (src_alpha == IM_SAMPLE_MAX)
562 *linep = STORE_COLOR;
563 else if (src_alpha) {
564 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
565 IM_WORK_T orig_alpha = linep->channel[alpha_channel];
566 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
567 for (ch = 0; ch < alpha_channel; ++ch) {
568 linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
569 + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
572 linep->channel[alpha_channel] = dest_alpha;
577 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
581 /* combine a line of image data with an output line, both the input
582 and output lines include an alpha channel.
584 Both input and output lines have I<channels> of data, channels
585 should be either 2 or 4.
589 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
590 int channels, i_img_dim count) {
592 int alpha_channel = channels - 1;
595 IM_WORK_T src_alpha = in->channel[alpha_channel];
597 if (src_alpha == IM_SAMPLE_MAX)
599 else if (src_alpha) {
600 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
601 IM_WORK_T orig_alpha = out->channel[alpha_channel];
602 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
604 for (ch = 0; ch < alpha_channel; ++ch) {
605 out->channel[ch] = ( src_alpha * in->channel[ch]
606 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
609 out->channel[alpha_channel] = dest_alpha;
618 /* combine a line of image data with an output line. The input line
619 includes an alpha channel, the output line has no alpha channel.
621 The input line has I<channels>+1 of color data. The output line
622 has I<channels> of color data.
626 IM_SUFFIX(combine_line_noalpha)
627 (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
631 IM_WORK_T src_alpha = in->channel[channels];
633 if (src_alpha == IM_SAMPLE_MAX)
635 else if (src_alpha) {
638 remains = IM_SAMPLE_MAX - src_alpha;
639 for (ch = 0; ch < channels; ++ch) {
640 out->channel[ch] = ( in->channel[ch] * src_alpha
641 + out->channel[ch] * remains) / IM_SAMPLE_MAX;
651 /* combine a line of image data with an output line, both the input
652 and output lines include an alpha channel.
654 Both input and output lines have I<channels> of data, channels
655 should be either 2 or 4.
657 This variant does not modify the output alpha channel.
661 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
662 int channels, i_img_dim count) {
664 int alpha_channel = channels - 1;
667 IM_WORK_T src_alpha = in->channel[alpha_channel];
669 if (src_alpha == IM_SAMPLE_MAX)
671 else if (src_alpha) {
672 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
673 IM_WORK_T orig_alpha = out->channel[alpha_channel];
674 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
676 for (ch = 0; ch < alpha_channel; ++ch) {
677 out->channel[ch] = ( src_alpha * in->channel[ch]
678 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
690 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
691 if (channels == 2 || channels == 4)
692 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
694 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
698 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
699 if (channels == 2 || channels == 4)
700 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
702 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
705 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
706 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
707 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
708 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
709 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
710 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
711 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
712 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
713 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
714 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
715 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
716 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
718 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
721 IM_SUFFIX(combine_alphablend),
722 IM_SUFFIX(combine_mult),
723 IM_SUFFIX(combine_dissolve),
724 IM_SUFFIX(combine_add),
725 IM_SUFFIX(combine_subtract),
726 IM_SUFFIX(combine_diff),
727 IM_SUFFIX(combine_lighten),
728 IM_SUFFIX(combine_darken),
729 IM_SUFFIX(combine_hue),
730 IM_SUFFIX(combine_sat),
731 IM_SUFFIX(combine_value),
732 IM_SUFFIX(combine_color)
738 =item i_get_combine(combine, color_func, fcolor_func)
743 void i_get_combine(int combine, i_fill_combine_f *color_func,
744 i_fill_combinef_f *fcolor_func) {
745 if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
748 *color_func = combines_8[combine];
749 *fcolor_func = combines_double[combine];
755 Three good references for implementing combining modes:
757 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
758 referenced as [svg1.2]
760 http://gimp-savvy.com/BOOK/index.html?node55.html
761 ("The Blending Modes", if it changes)
762 referenced as [savvy]
764 http://www.pegtop.net/delphi/articles/blendmodes/
765 referenced as [pegtop]
767 Where differences exist, I follow the SVG practice, the gimp
768 practice, and lastly pegtop.
773 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
774 IM_SUFFIX(combine_line)(out, in, channels, count);
778 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
779 Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
784 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
787 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
790 IM_COLOR *outp = out;
791 i_img_dim work_count = count;
792 int color_channels = i_color_channels(channels);
794 if (i_has_alpha(channels)) {
795 while (work_count--) {
796 IM_WORK_T orig_alpha = outp->channel[color_channels];
797 IM_WORK_T src_alpha = inp->channel[color_channels];
800 IM_WORK_T dest_alpha = src_alpha + orig_alpha
801 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
803 for (ch = 0; ch < color_channels; ++ch) {
805 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
807 + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
808 + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
809 / IM_SAMPLE_MAX / dest_alpha;
811 outp->channel[color_channels] = dest_alpha;
818 while (work_count--) {
819 IM_WORK_T src_alpha = inp->channel[color_channels];
820 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
823 for (ch = 0; ch < color_channels; ++ch) {
825 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
826 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
836 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
837 int color_channels = i_color_channels(channels);
840 if (i_has_alpha(channels)) {
842 if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
843 for (ch = 0; ch < color_channels; ++ch) {
844 out->channel[ch] = in->channel[ch];
846 out->channel[color_channels] = IM_SAMPLE_MAX;
854 if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
855 for (ch = 0; ch < color_channels; ++ch) {
856 out->channel[ch] = in->channel[ch];
866 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
868 Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
873 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
875 int color_channels = i_color_channels(channels);
876 i_img_dim work_count = count;
878 IM_COLOR *outp = out;
880 if (i_has_alpha(channels)) {
881 while (work_count--) {
882 IM_WORK_T src_alpha = inp->channel[color_channels];
884 IM_WORK_T orig_alpha = outp->channel[color_channels];
885 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
886 if (dest_alpha > IM_SAMPLE_MAX)
887 dest_alpha = IM_SAMPLE_MAX;
888 for (ch = 0; ch < color_channels; ++ch) {
889 IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
890 if (total > IM_SAMPLE_MAX)
891 total = IM_SAMPLE_MAX;
892 outp->channel[ch] = total;
894 outp->channel[color_channels] = dest_alpha;
902 while (work_count--) {
903 IM_WORK_T src_alpha = inp->channel[color_channels];
905 for (ch = 0; ch < color_channels; ++ch) {
906 IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
907 if (total > IM_SAMPLE_MAX)
908 total = IM_SAMPLE_MAX;
909 outp->channel[ch] = total;
920 [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
921 it as max(A-B, 0). [svg1.2] doesn't cover it.
923 [savvy] doesn't document how it works with an alpha channel. GIMP
924 actually seems to calculate the final value then use the alpha
925 channel to apply that to the target.
928 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
930 IM_COLOR const *inp = in;
931 IM_COLOR *outp = out;
932 i_img_dim work_count = count;
933 int color_channels = i_color_channels(channels);
935 if (i_has_alpha(channels)) {
936 while (work_count--) {
937 IM_WORK_T src_alpha = inp->channel[color_channels];
939 IM_WORK_T orig_alpha = outp->channel[color_channels];
940 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
941 if (dest_alpha > IM_SAMPLE_MAX)
942 dest_alpha = IM_SAMPLE_MAX;
943 for (ch = 0; ch < color_channels; ++ch) {
945 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
949 outp->channel[ch] = total;
951 outp->channel[color_channels] = dest_alpha;
958 while (work_count--) {
959 IM_WORK_T src_alpha = inp->channel[color_channels];
961 for (ch = 0; ch < color_channels; ++ch) {
962 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
965 outp->channel[ch] = total;
975 #define IM_abs(x) abs(x)
977 #define IM_abs(x) fabs(x)
981 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
982 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
983 Da' = Sa + Da - Sa.Da
986 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
988 IM_COLOR const *inp = in;
989 IM_COLOR *outp = out;
990 i_img_dim work_count = count;
991 int color_channels = i_color_channels(channels);
993 if (i_has_alpha(channels)) {
994 while (work_count--) {
995 IM_WORK_T src_alpha = inp->channel[color_channels];
997 IM_WORK_T orig_alpha = outp->channel[color_channels];
998 IM_WORK_T dest_alpha = src_alpha + orig_alpha
999 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1000 for (ch = 0; ch < color_channels; ++ch) {
1001 IM_WORK_T src = inp->channel[ch] * src_alpha;
1002 IM_WORK_T orig = outp->channel[ch] * orig_alpha;
1003 IM_WORK_T src_da = src * orig_alpha;
1004 IM_WORK_T dest_sa = orig * src_alpha;
1005 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1006 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1008 outp->channel[color_channels] = dest_alpha;
1015 while (work_count--) {
1016 IM_WORK_T src_alpha = inp->channel[color_channels];
1018 for (ch = 0; ch < color_channels; ++ch) {
1019 IM_WORK_T src = inp->channel[ch] * src_alpha;
1020 IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1021 IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1022 IM_WORK_T dest_sa = orig * src_alpha;
1023 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1024 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1036 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1037 Da' = Sa + Da - Sa.Da
1041 Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1042 = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1046 Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1047 = min(Sca, Dc.Sa) + Dc(1-Sa)
1048 = Sa.min(Sc, Dc) + Dc - Dc.Sa
1053 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
1056 IM_COLOR const *inp = in;
1057 IM_COLOR *outp = out;
1058 i_img_dim work_count = count;
1059 int color_channels = i_color_channels(channels);
1061 if (i_has_alpha(channels)) {
1062 while (work_count--) {
1063 IM_WORK_T src_alpha = inp->channel[color_channels];
1066 IM_WORK_T orig_alpha = outp->channel[color_channels];
1067 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1068 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1069 for (ch = 0; ch < color_channels; ++ch) {
1070 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1071 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1072 IM_WORK_T ScaDa = Sca * orig_alpha;
1073 IM_WORK_T DcaSa = Dca * src_alpha;
1074 IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1077 minc + (Sca + Dca) * IM_SAMPLE_MAX
1079 ) / (IM_SAMPLE_MAX * dest_alpha);
1081 outp->channel[color_channels] = dest_alpha;
1088 while (work_count--) {
1089 IM_WORK_T src_alpha = inp->channel[color_channels];
1092 for (ch = 0; ch < color_channels; ++ch) {
1093 IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1094 ? outp->channel[ch] : inp->channel[ch];
1098 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1109 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1111 IM_COLOR const *inp = in;
1112 IM_COLOR *outp = out;
1113 i_img_dim work_count = count;
1114 int color_channels = i_color_channels(channels);
1116 if (i_has_alpha(channels)) {
1117 while (work_count--) {
1118 IM_WORK_T src_alpha = inp->channel[color_channels];
1121 IM_WORK_T orig_alpha = outp->channel[color_channels];
1122 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1123 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1124 for (ch = 0; ch < color_channels; ++ch) {
1125 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1126 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1127 IM_WORK_T ScaDa = Sca * orig_alpha;
1128 IM_WORK_T DcaSa = Dca * src_alpha;
1129 IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1132 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1134 ) / (IM_SAMPLE_MAX * dest_alpha);
1136 outp->channel[color_channels] = dest_alpha;
1143 while (work_count--) {
1144 IM_WORK_T src_alpha = inp->channel[color_channels];
1147 for (ch = 0; ch < color_channels; ++ch) {
1148 IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1149 ? outp->channel[ch] : inp->channel[ch];
1153 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1164 #define IM_RGB_TO_HSV i_rgb_to_hsv
1165 #define IM_HSV_TO_RGB i_hsv_to_rgb
1167 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1168 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1172 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1175 IM_COLOR const *outp = out;
1176 i_img_dim work_count = count;
1178 if (i_has_alpha(channels)) {
1179 while (work_count--) {
1182 /* only transfer hue if there's saturation */
1183 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1186 /* and no point in setting the target hue if the target has no sat */
1187 if (inp->channel[1]) {
1188 inp->channel[0] = c.channel[0];
1190 inp->channel[3] = c.channel[3];
1193 inp->channel[3] = 0;
1197 inp->channel[3] = 0;
1205 while (work_count--) {
1208 /* only transfer hue if there's saturation */
1209 if (c.channel[1] && inp->channel[3]) {
1212 /* and no point in setting the target hue if the target has no sat */
1213 if (inp->channel[1]) {
1214 inp->channel[0] = c.channel[0];
1216 inp->channel[3] = c.channel[3];
1220 inp->channel[3] = 0;
1228 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1233 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1236 IM_COLOR const *outp = out;
1237 i_img_dim work_count = count;
1239 while (work_count--) {
1244 inp->channel[1] = c.channel[1];
1246 inp->channel[3] = c.channel[3];
1251 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1256 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1259 IM_COLOR const *outp = out;
1260 i_img_dim work_count = count;
1262 while (work_count--) {
1267 inp->channel[2] = c.channel[2];
1269 inp->channel[3] = c.channel[3];
1275 /* all images have a "value channel" - for greyscale it's the only
1277 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1281 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1284 IM_COLOR const *outp = out;
1285 i_img_dim work_count = count;
1287 while (work_count--) {
1292 inp->channel[0] = c.channel[0];
1293 inp->channel[1] = c.channel[1];
1295 inp->channel[3] = c.channel[3];
1300 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1304 #undef IM_RGB_TO_HSV
1305 #undef IM_HSV_TO_RGB