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] * (255 - *src)
337 + srcc->channel[ch] * *src) / 255.0;
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;
488 int color_alpha = color->channel[im->channels];
491 #define STORE_COLOR (*color)
495 for (ch = 0; ch < channels; ++ch) {
496 fcolor.channel[ch] = color->channel[ch] / 255.0;
498 #define STORE_COLOR fcolor
502 if (color_alpha == 0xFF) {
503 while (fetch_offset < width && *src == 0xFF) {
504 *linep++ = STORE_COLOR;
509 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
510 while (fetch_offset < width) {
512 IM_WORK_T alpha = *src++ * color_alpha / 255;
514 IM_WORK_T alpha = *src++ * color_alpha / (255.0 * 255.0);
516 if (alpha == IM_SAMPLE_MAX)
517 *linep = STORE_COLOR;
519 for (ch = 0; ch < channels; ++ch) {
520 linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
521 + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
527 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
532 IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
533 i_img_dim width, unsigned char const *src,
534 i_color const *color) {
535 IM_COLOR *linep = r->IM_SUFFIX(line);
537 int alpha_channel = r->im->channels - 1;
538 i_img_dim fetch_offset;
539 int color_alpha = color->channel[alpha_channel];
542 #define STORE_COLOR (*color)
546 for (ch = 0; ch < r->im->channels; ++ch) {
547 fcolor.channel[ch] = color->channel[ch] / 255.0;
549 #define STORE_COLOR fcolor
553 if (color->channel[alpha_channel] == 0xFF) {
554 while (fetch_offset < width && *src == 0xFF) {
555 *linep++ = STORE_COLOR;
560 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
561 while (fetch_offset < width) {
563 IM_WORK_T src_alpha = *src++ * color_alpha / 255;
565 IM_WORK_T src_alpha = *src++ * color_alpha / (255.0 * 255.0);
567 if (src_alpha == IM_SAMPLE_MAX)
568 *linep = STORE_COLOR;
569 else if (src_alpha) {
570 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
571 IM_WORK_T orig_alpha = linep->channel[alpha_channel];
572 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
573 for (ch = 0; ch < alpha_channel; ++ch) {
574 linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
575 + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
578 linep->channel[alpha_channel] = dest_alpha;
583 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
587 /* combine a line of image data with an output line, both the input
588 and output lines include an alpha channel.
590 Both input and output lines have I<channels> of data, channels
591 should be either 2 or 4.
595 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
596 int channels, i_img_dim count) {
598 int alpha_channel = channels - 1;
601 IM_WORK_T src_alpha = in->channel[alpha_channel];
603 if (src_alpha == IM_SAMPLE_MAX)
605 else if (src_alpha) {
606 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
607 IM_WORK_T orig_alpha = out->channel[alpha_channel];
608 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
610 for (ch = 0; ch < alpha_channel; ++ch) {
611 out->channel[ch] = ( src_alpha * in->channel[ch]
612 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
615 out->channel[alpha_channel] = dest_alpha;
624 /* combine a line of image data with an output line. The input line
625 includes an alpha channel, the output line has no alpha channel.
627 The input line has I<channels>+1 of color data. The output line
628 has I<channels> of color data.
632 IM_SUFFIX(combine_line_noalpha)
633 (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
637 IM_WORK_T src_alpha = in->channel[channels];
639 if (src_alpha == IM_SAMPLE_MAX)
641 else if (src_alpha) {
644 remains = IM_SAMPLE_MAX - src_alpha;
645 for (ch = 0; ch < channels; ++ch) {
646 out->channel[ch] = ( in->channel[ch] * src_alpha
647 + out->channel[ch] * remains) / IM_SAMPLE_MAX;
657 /* combine a line of image data with an output line, both the input
658 and output lines include an alpha channel.
660 Both input and output lines have I<channels> of data, channels
661 should be either 2 or 4.
663 This variant does not modify the output alpha channel.
667 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
668 int channels, i_img_dim count) {
670 int alpha_channel = channels - 1;
673 IM_WORK_T src_alpha = in->channel[alpha_channel];
675 if (src_alpha == IM_SAMPLE_MAX)
677 else if (src_alpha) {
678 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
679 IM_WORK_T orig_alpha = out->channel[alpha_channel];
680 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
682 for (ch = 0; ch < alpha_channel; ++ch) {
683 out->channel[ch] = ( src_alpha * in->channel[ch]
684 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
696 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
697 if (channels == 2 || channels == 4)
698 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
700 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
704 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
705 if (channels == 2 || channels == 4)
706 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
708 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
711 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
712 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
713 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
714 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
715 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
716 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
717 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
718 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
719 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
720 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
721 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
722 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
724 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
727 IM_SUFFIX(combine_alphablend),
728 IM_SUFFIX(combine_mult),
729 IM_SUFFIX(combine_dissolve),
730 IM_SUFFIX(combine_add),
731 IM_SUFFIX(combine_subtract),
732 IM_SUFFIX(combine_diff),
733 IM_SUFFIX(combine_lighten),
734 IM_SUFFIX(combine_darken),
735 IM_SUFFIX(combine_hue),
736 IM_SUFFIX(combine_sat),
737 IM_SUFFIX(combine_value),
738 IM_SUFFIX(combine_color)
744 =item i_get_combine(combine, color_func, fcolor_func)
749 void i_get_combine(int combine, i_fill_combine_f *color_func,
750 i_fill_combinef_f *fcolor_func) {
751 if (combine < 0 || combine >= sizeof(combines_8) / sizeof(*combines_8))
754 *color_func = combines_8[combine];
755 *fcolor_func = combines_double[combine];
761 Three good references for implementing combining modes:
763 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
764 referenced as [svg1.2]
766 http://gimp-savvy.com/BOOK/index.html?node55.html
767 ("The Blending Modes", if it changes)
768 referenced as [savvy]
770 http://www.pegtop.net/delphi/articles/blendmodes/
771 referenced as [pegtop]
773 Where differences exist, I follow the SVG practice, the gimp
774 practice, and lastly pegtop.
779 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
780 IM_SUFFIX(combine_line)(out, in, channels, count);
784 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
785 Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
790 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
793 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
796 IM_COLOR *outp = out;
797 i_img_dim work_count = count;
798 int color_channels = i_color_channels(channels);
800 if (i_has_alpha(channels)) {
801 while (work_count--) {
802 IM_WORK_T orig_alpha = outp->channel[color_channels];
803 IM_WORK_T src_alpha = inp->channel[color_channels];
806 IM_WORK_T dest_alpha = src_alpha + orig_alpha
807 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
809 for (ch = 0; ch < color_channels; ++ch) {
811 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
813 + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
814 + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
815 / IM_SAMPLE_MAX / dest_alpha;
817 outp->channel[color_channels] = dest_alpha;
824 while (work_count--) {
825 IM_WORK_T src_alpha = inp->channel[color_channels];
826 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
829 for (ch = 0; ch < color_channels; ++ch) {
831 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
832 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
842 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
843 int color_channels = i_color_channels(channels);
846 if (i_has_alpha(channels)) {
848 if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
849 for (ch = 0; ch < color_channels; ++ch) {
850 out->channel[ch] = in->channel[ch];
852 out->channel[color_channels] = IM_SAMPLE_MAX;
860 if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
861 for (ch = 0; ch < color_channels; ++ch) {
862 out->channel[ch] = in->channel[ch];
872 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
874 Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
879 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
881 int color_channels = i_color_channels(channels);
882 i_img_dim work_count = count;
884 IM_COLOR *outp = out;
886 if (i_has_alpha(channels)) {
887 while (work_count--) {
888 IM_WORK_T src_alpha = inp->channel[color_channels];
890 IM_WORK_T orig_alpha = outp->channel[color_channels];
891 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
892 if (dest_alpha > IM_SAMPLE_MAX)
893 dest_alpha = IM_SAMPLE_MAX;
894 for (ch = 0; ch < color_channels; ++ch) {
895 IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
896 if (total > IM_SAMPLE_MAX)
897 total = IM_SAMPLE_MAX;
898 outp->channel[ch] = total;
900 outp->channel[color_channels] = dest_alpha;
908 while (work_count--) {
909 IM_WORK_T src_alpha = inp->channel[color_channels];
911 for (ch = 0; ch < color_channels; ++ch) {
912 IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
913 if (total > IM_SAMPLE_MAX)
914 total = IM_SAMPLE_MAX;
915 outp->channel[ch] = total;
926 [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
927 it as max(A-B, 0). [svg1.2] doesn't cover it.
929 [savvy] doesn't document how it works with an alpha channel. GIMP
930 actually seems to calculate the final value then use the alpha
931 channel to apply that to the target.
934 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
936 IM_COLOR const *inp = in;
937 IM_COLOR *outp = out;
938 i_img_dim work_count = count;
939 int color_channels = i_color_channels(channels);
941 if (i_has_alpha(channels)) {
942 while (work_count--) {
943 IM_WORK_T src_alpha = inp->channel[color_channels];
945 IM_WORK_T orig_alpha = outp->channel[color_channels];
946 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
947 if (dest_alpha > IM_SAMPLE_MAX)
948 dest_alpha = IM_SAMPLE_MAX;
949 for (ch = 0; ch < color_channels; ++ch) {
951 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
955 outp->channel[ch] = total;
957 outp->channel[color_channels] = dest_alpha;
964 while (work_count--) {
965 IM_WORK_T src_alpha = inp->channel[color_channels];
967 for (ch = 0; ch < color_channels; ++ch) {
968 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
971 outp->channel[ch] = total;
981 #define IM_abs(x) abs(x)
983 #define IM_abs(x) fabs(x)
987 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
988 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
989 Da' = Sa + Da - Sa.Da
992 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
994 IM_COLOR const *inp = in;
995 IM_COLOR *outp = out;
996 i_img_dim work_count = count;
997 int color_channels = i_color_channels(channels);
999 if (i_has_alpha(channels)) {
1000 while (work_count--) {
1001 IM_WORK_T src_alpha = inp->channel[color_channels];
1003 IM_WORK_T orig_alpha = outp->channel[color_channels];
1004 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1005 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1006 for (ch = 0; ch < color_channels; ++ch) {
1007 IM_WORK_T src = inp->channel[ch] * src_alpha;
1008 IM_WORK_T orig = outp->channel[ch] * orig_alpha;
1009 IM_WORK_T src_da = src * orig_alpha;
1010 IM_WORK_T dest_sa = orig * src_alpha;
1011 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1012 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1014 outp->channel[color_channels] = dest_alpha;
1021 while (work_count--) {
1022 IM_WORK_T src_alpha = inp->channel[color_channels];
1024 for (ch = 0; ch < color_channels; ++ch) {
1025 IM_WORK_T src = inp->channel[ch] * src_alpha;
1026 IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1027 IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1028 IM_WORK_T dest_sa = orig * src_alpha;
1029 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1030 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1042 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1043 Da' = Sa + Da - Sa.Da
1047 Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1048 = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1052 Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1053 = min(Sca, Dc.Sa) + Dc(1-Sa)
1054 = Sa.min(Sc, Dc) + Dc - Dc.Sa
1059 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
1062 IM_COLOR const *inp = in;
1063 IM_COLOR *outp = out;
1064 i_img_dim work_count = count;
1065 int color_channels = i_color_channels(channels);
1067 if (i_has_alpha(channels)) {
1068 while (work_count--) {
1069 IM_WORK_T src_alpha = inp->channel[color_channels];
1072 IM_WORK_T orig_alpha = outp->channel[color_channels];
1073 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1074 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1075 for (ch = 0; ch < color_channels; ++ch) {
1076 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1077 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1078 IM_WORK_T ScaDa = Sca * orig_alpha;
1079 IM_WORK_T DcaSa = Dca * src_alpha;
1080 IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1083 minc + (Sca + Dca) * IM_SAMPLE_MAX
1085 ) / (IM_SAMPLE_MAX * dest_alpha);
1087 outp->channel[color_channels] = dest_alpha;
1094 while (work_count--) {
1095 IM_WORK_T src_alpha = inp->channel[color_channels];
1098 for (ch = 0; ch < color_channels; ++ch) {
1099 IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1100 ? outp->channel[ch] : inp->channel[ch];
1104 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1115 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1117 IM_COLOR const *inp = in;
1118 IM_COLOR *outp = out;
1119 i_img_dim work_count = count;
1120 int color_channels = i_color_channels(channels);
1122 if (i_has_alpha(channels)) {
1123 while (work_count--) {
1124 IM_WORK_T src_alpha = inp->channel[color_channels];
1127 IM_WORK_T orig_alpha = outp->channel[color_channels];
1128 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1129 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1130 for (ch = 0; ch < color_channels; ++ch) {
1131 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1132 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1133 IM_WORK_T ScaDa = Sca * orig_alpha;
1134 IM_WORK_T DcaSa = Dca * src_alpha;
1135 IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1138 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1140 ) / (IM_SAMPLE_MAX * dest_alpha);
1142 outp->channel[color_channels] = dest_alpha;
1149 while (work_count--) {
1150 IM_WORK_T src_alpha = inp->channel[color_channels];
1153 for (ch = 0; ch < color_channels; ++ch) {
1154 IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1155 ? outp->channel[ch] : inp->channel[ch];
1159 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1170 #define IM_RGB_TO_HSV i_rgb_to_hsv
1171 #define IM_HSV_TO_RGB i_hsv_to_rgb
1173 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1174 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1178 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1181 IM_COLOR const *outp = out;
1182 i_img_dim work_count = count;
1184 if (i_has_alpha(channels)) {
1185 while (work_count--) {
1188 /* only transfer hue if there's saturation */
1189 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1192 /* and no point in setting the target hue if the target has no sat */
1193 if (inp->channel[1]) {
1194 inp->channel[0] = c.channel[0];
1196 inp->channel[3] = c.channel[3];
1199 inp->channel[3] = 0;
1203 inp->channel[3] = 0;
1211 while (work_count--) {
1214 /* only transfer hue if there's saturation */
1215 if (c.channel[1] && inp->channel[3]) {
1218 /* and no point in setting the target hue if the target has no sat */
1219 if (inp->channel[1]) {
1220 inp->channel[0] = c.channel[0];
1222 inp->channel[3] = c.channel[3];
1226 inp->channel[3] = 0;
1234 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1239 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1242 IM_COLOR const *outp = out;
1243 i_img_dim work_count = count;
1245 while (work_count--) {
1250 inp->channel[1] = c.channel[1];
1252 inp->channel[3] = c.channel[3];
1257 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1262 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1265 IM_COLOR const *outp = out;
1266 i_img_dim work_count = count;
1268 while (work_count--) {
1273 inp->channel[2] = c.channel[2];
1275 inp->channel[3] = c.channel[3];
1281 /* all images have a "value channel" - for greyscale it's the only
1283 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1287 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1290 IM_COLOR const *outp = out;
1291 i_img_dim work_count = count;
1293 while (work_count--) {
1298 inp->channel[0] = c.channel[0];
1299 inp->channel[1] = c.channel[1];
1301 inp->channel[3] = c.channel[3];
1306 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1310 #undef IM_RGB_TO_HSV
1311 #undef IM_HSV_TO_RGB