6 #define RENDER_MAGIC 0x765AE
8 typedef void (*render_color_f)(i_render *, int, int, int, 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, 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);
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, 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);
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);
39 i_render_init(i_render *r, i_img *im, int width) {
40 r->magic = RENDER_MAGIC;
42 r->line_width = width;
44 r->line_double = NULL;
45 r->fill_width = width;
46 r->fill_line_8 = NULL;
47 r->fill_line_double = NULL;
51 i_render_done(i_render *r) {
55 myfree(r->line_double);
57 myfree(r->fill_line_8);
58 if (r->fill_line_double)
59 myfree(r->fill_line_double);
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)
72 r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
74 r->line_8 = mymalloc(sizeof(i_color) * new_width);
76 myfree(r->line_double);
77 r->line_double = NULL;
82 r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
84 r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
87 r->line_double = NULL;
91 r->line_width = new_width;
96 r->line_8 = mymalloc(sizeof(i_color) * r->line_width);
98 myfree(r->line_double);
99 r->line_double = NULL;
104 r->line_double = mymalloc(sizeof(i_fcolor) * r->line_width);
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)
122 r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
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;
131 if (r->fill_line_double)
132 r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
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;
141 r->fill_width = new_width;
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;
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;
164 i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
165 i_color const *color) {
167 if (y < 0 || y >= im->ysize)
174 if (x + width > im->xsize) {
175 width = im->xsize - x;
177 if (x >= im->xsize || x + width <= 0 || width <= 0)
180 /* avoid as much work as we can */
181 while (width > 0 && *src == 0) {
186 while (width > 0 && src[width-1] == 0) {
192 alloc_line(r, width, r->im->bits <= 8);
194 #code r->im->bits <= 8
195 /*if (r->IM_SUFFIX(line) == NULL)
196 r->IM_SUFFIX(line) = mymalloc(sizeof(IM_COLOR) * r->width);*/
197 (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
202 i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
205 int fill_channels = im->channels;
207 if (fill_channels == 1 || fill_channels == 3)
210 if (y < 0 || y >= im->ysize)
217 if (x + width > im->xsize) {
218 width = im->xsize - x;
220 if (x >= im->xsize || x + width <= 0 || width <= 0)
224 /* avoid as much work as we can */
225 while (width > 0 && *src == 0) {
230 while (width > 0 && src[width-1] == 0) {
237 alloc_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
238 alloc_fill_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
240 #code r->im->bits <= 8 && fill->f_fill_with_color
241 if (IM_FILL_COMBINE(fill)) {
242 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
243 IM_COLOR *destc = r->IM_SUFFIX(line);
244 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
246 unsigned char const *srcc = src;
247 IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
248 int work_width = width;
251 fillc->channel[fill_channels-1] = 0;
253 else if (*srcc != 255) {
254 fillc->channel[fill_channels-1] =
255 fillc->channel[fill_channels-1] * *srcc / 255;
262 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
263 IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
267 int work_width = width;
268 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
269 IM_COLOR *destc = r->IM_SUFFIX(line);
272 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
273 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
276 /* just replace it */
280 for (ch = 0; ch < im->channels; ++ch) {
281 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
282 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
283 destc->channel[ch] = IM_LIMIT(work);
293 else { /* if (src) */
294 IM_FILL_FILLER(fill)(fill, x, y, width, r->im->channels, r->IM_SUFFIX(line));
297 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
302 dump_src(const char *note, unsigned char const *src, int width) {
304 printf("%s - %p/%d\n", note, src, width);
305 for (i = 0; i < width; ++i) {
306 printf("%02x ", src[i]);
314 IM_RENDER_LINE(i_render *r, int x, int y, int width, const IM_SAMPLE_T *src,
315 IM_COLOR *line, IM_FILL_COMBINE_F combine) {
317 int src_chans = im->channels;
319 /* src must always have an alpha channel */
320 if (src_chans == 1 || src_chans == 3)
323 if (y < 0 || y >= im->ysize)
331 if (x + width > im->xsize)
332 width = r->im->xsize - x;
335 alloc_line(r, width, 1);
337 alloc_line(r, width, 0);
342 int work_width = width;
343 IM_COLOR *linep = line;
344 const IM_SAMPLE_T *srcp = src;
345 int alpha_chan = src_chans - 1;
349 if (*srcp != IM_SAMPLE_MAX)
350 linep->channel[alpha_chan] =
351 linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
354 linep->channel[alpha_chan] = 0;
361 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
362 combine(r->IM_SUFFIX(line), line, im->channels, width);
363 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
367 int work_width = width;
368 IM_COLOR *srcc = line;
369 IM_COLOR *destc = r->IM_SUFFIX(line);
371 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
374 /* just replace it */
379 for (ch = 0; ch < im->channels; ++ch) {
380 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
381 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
382 destc->channel[ch] = IM_LIMIT(work);
391 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
394 IM_PLIN(im, x, x+width, y, line);
401 IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width,
402 unsigned char const *src, i_color const *color) {
404 IM_COLOR *linep = r->IM_SUFFIX(line);
405 int ch, channels = im->channels;
409 #define STORE_COLOR (*color)
413 for (ch = 0; ch < channels; ++ch) {
414 fcolor.channel[ch] = color->channel[ch] / 255.0;
416 #define STORE_COLOR fcolor
420 while (fetch_offset < width && *src == 0xFF) {
421 *linep++ = STORE_COLOR;
425 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
426 while (fetch_offset < width) {
428 IM_WORK_T alpha = *src++;
430 IM_WORK_T alpha = *src++ / 255.0;
432 if (alpha == IM_SAMPLE_MAX)
433 *linep = STORE_COLOR;
435 for (ch = 0; ch < channels; ++ch) {
436 linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
437 + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
443 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
448 IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width,
449 unsigned char const *src, i_color const *color) {
450 IM_COLOR *linep = r->IM_SUFFIX(line);
452 int alpha_channel = r->im->channels - 1;
456 #define STORE_COLOR (*color)
460 for (ch = 0; ch < r->im->channels; ++ch) {
461 fcolor.channel[ch] = color->channel[ch] / 255.0;
463 #define STORE_COLOR fcolor
467 while (fetch_offset < width && *src == 0xFF) {
468 *linep++ = STORE_COLOR;
472 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
473 while (fetch_offset < width) {
475 IM_WORK_T src_alpha = *src++;
477 IM_WORK_T src_alpha = *src++ / 255.0;
479 if (src_alpha == IM_SAMPLE_MAX)
480 *linep = STORE_COLOR;
481 else if (src_alpha) {
482 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
483 IM_WORK_T orig_alpha = linep->channel[alpha_channel];
484 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
485 for (ch = 0; ch < alpha_channel; ++ch) {
486 linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
487 + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
490 linep->channel[alpha_channel] = dest_alpha;
495 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
499 /* combine a line of image data with an output line, both the input
500 and output lines include an alpha channel.
502 Both input and output lines have I<channels> of data, channels
503 should be either 2 or 4.
507 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
508 int channels, int count) {
510 int alpha_channel = channels - 1;
513 IM_WORK_T src_alpha = in->channel[alpha_channel];
515 if (src_alpha == IM_SAMPLE_MAX)
517 else if (src_alpha) {
518 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
519 IM_WORK_T orig_alpha = out->channel[alpha_channel];
520 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
522 for (ch = 0; ch < alpha_channel; ++ch) {
523 out->channel[ch] = ( src_alpha * in->channel[ch]
524 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
527 out->channel[alpha_channel] = dest_alpha;
536 /* combine a line of image data with an output line. The input line
537 includes an alpha channel, the output line has no alpha channel.
539 The input line has I<channels>+1 of color data. The output line
540 has I<channels> of color data.
544 IM_SUFFIX(combine_line_noalpha)
545 (IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
549 IM_WORK_T src_alpha = in->channel[channels];
551 if (src_alpha == IM_SAMPLE_MAX)
553 else if (src_alpha) {
556 remains = IM_SAMPLE_MAX - src_alpha;
557 for (ch = 0; ch < channels; ++ch) {
558 out->channel[ch] = ( in->channel[ch] * src_alpha
559 + out->channel[ch] * remains) / IM_SAMPLE_MAX;
569 /* combine a line of image data with an output line, both the input
570 and output lines include an alpha channel.
572 Both input and output lines have I<channels> of data, channels
573 should be either 2 or 4.
575 This variant does not modify the output alpha channel.
579 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
580 int channels, int count) {
582 int alpha_channel = channels - 1;
585 IM_WORK_T src_alpha = in->channel[alpha_channel];
587 if (src_alpha == IM_SAMPLE_MAX)
589 else if (src_alpha) {
590 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
591 IM_WORK_T orig_alpha = out->channel[alpha_channel];
592 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
594 for (ch = 0; ch < alpha_channel; ++ch) {
595 out->channel[ch] = ( src_alpha * in->channel[ch]
596 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
608 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
609 if (channels == 2 || channels == 4)
610 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
612 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
616 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
617 if (channels == 2 || channels == 4)
618 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
620 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
623 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, int);
624 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, int);
625 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, int);
626 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, int);
627 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, int);
628 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, int);
629 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, int);
630 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, int);
631 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, int);
632 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, int);
633 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, int);
634 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, int);
636 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
639 IM_SUFFIX(combine_alphablend),
640 IM_SUFFIX(combine_mult),
641 IM_SUFFIX(combine_dissolve),
642 IM_SUFFIX(combine_add),
643 IM_SUFFIX(combine_subtract),
644 IM_SUFFIX(combine_diff),
645 IM_SUFFIX(combine_lighten),
646 IM_SUFFIX(combine_darken),
647 IM_SUFFIX(combine_hue),
648 IM_SUFFIX(combine_sat),
649 IM_SUFFIX(combine_value),
650 IM_SUFFIX(combine_color)
656 =item i_get_combine(combine, color_func, fcolor_func)
661 void i_get_combine(int combine, i_fill_combine_f *color_func,
662 i_fill_combinef_f *fcolor_func) {
663 if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
666 *color_func = combines_8[combine];
667 *fcolor_func = combines_double[combine];
673 Three good references for implementing combining modes:
675 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
676 referenced as [svg1.2]
678 http://gimp-savvy.com/BOOK/index.html?node55.html
679 ("The Blending Modes", if it changes)
680 referenced as [savvy]
682 http://www.pegtop.net/delphi/articles/blendmodes/
683 referenced as [pegtop]
685 Where differences exist, I follow the SVG practice, the gimp
686 practice, and lastly pegtop.
691 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
692 IM_SUFFIX(combine_line)(out, in, channels, count);
696 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
697 Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
702 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
705 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
708 IM_COLOR *outp = out;
709 int work_count = count;
710 int color_channels = i_color_channels(channels);
712 if (i_has_alpha(channels)) {
713 while (work_count--) {
714 IM_WORK_T orig_alpha = outp->channel[color_channels];
715 IM_WORK_T src_alpha = inp->channel[color_channels];
718 IM_WORK_T dest_alpha = src_alpha + orig_alpha
719 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
721 for (ch = 0; ch < color_channels; ++ch) {
723 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
725 + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
726 + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
727 / IM_SAMPLE_MAX / dest_alpha;
729 outp->channel[color_channels] = dest_alpha;
736 while (work_count--) {
737 IM_WORK_T src_alpha = inp->channel[color_channels];
738 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
741 for (ch = 0; ch < color_channels; ++ch) {
743 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
744 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
754 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
755 int color_channels = i_color_channels(channels);
758 if (i_has_alpha(channels)) {
760 if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
761 for (ch = 0; ch < color_channels; ++ch) {
762 out->channel[ch] = in->channel[ch];
764 out->channel[color_channels] = IM_SAMPLE_MAX;
772 if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
773 for (ch = 0; ch < color_channels; ++ch) {
774 out->channel[ch] = in->channel[ch];
784 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
786 Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
791 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
793 int color_channels = i_color_channels(channels);
794 int work_count = count;
796 IM_COLOR *outp = out;
798 if (i_has_alpha(channels)) {
799 while (work_count--) {
800 IM_WORK_T src_alpha = inp->channel[color_channels];
802 IM_WORK_T orig_alpha = outp->channel[color_channels];
803 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
804 if (dest_alpha > IM_SAMPLE_MAX)
805 dest_alpha = IM_SAMPLE_MAX;
806 for (ch = 0; ch < color_channels; ++ch) {
807 IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
808 if (total > IM_SAMPLE_MAX)
809 total = IM_SAMPLE_MAX;
810 outp->channel[ch] = total;
812 outp->channel[color_channels] = dest_alpha;
820 while (work_count--) {
821 IM_WORK_T src_alpha = inp->channel[color_channels];
823 for (ch = 0; ch < color_channels; ++ch) {
824 IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
825 if (total > IM_SAMPLE_MAX)
826 total = IM_SAMPLE_MAX;
827 outp->channel[ch] = total;
838 [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
839 it as max(A-B, 0). [svg1.2] doesn't cover it.
841 [savvy] doesn't document how it works with an alpha channel. GIMP
842 actually seems to calculate the final value then use the alpha
843 channel to apply that to the target.
846 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
848 IM_COLOR const *inp = in;
849 IM_COLOR *outp = out;
850 int work_count = count;
851 int color_channels = i_color_channels(channels);
853 if (i_has_alpha(channels)) {
854 while (work_count--) {
855 IM_WORK_T src_alpha = inp->channel[color_channels];
857 IM_WORK_T orig_alpha = outp->channel[color_channels];
858 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
859 if (dest_alpha > IM_SAMPLE_MAX)
860 dest_alpha = IM_SAMPLE_MAX;
861 for (ch = 0; ch < color_channels; ++ch) {
863 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
867 outp->channel[ch] = total;
869 outp->channel[color_channels] = dest_alpha;
876 while (work_count--) {
877 IM_WORK_T src_alpha = inp->channel[color_channels];
879 for (ch = 0; ch < color_channels; ++ch) {
880 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
883 outp->channel[ch] = total;
893 #define IM_abs(x) abs(x)
895 #define IM_abs(x) fabs(x)
899 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
900 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
901 Da' = Sa + Da - Sa.Da
904 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
906 IM_COLOR const *inp = in;
907 IM_COLOR *outp = out;
908 int work_count = count;
909 int color_channels = i_color_channels(channels);
911 if (i_has_alpha(channels)) {
912 while (work_count--) {
913 IM_WORK_T src_alpha = inp->channel[color_channels];
915 IM_WORK_T orig_alpha = outp->channel[color_channels];
916 IM_WORK_T dest_alpha = src_alpha + orig_alpha
917 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
918 for (ch = 0; ch < color_channels; ++ch) {
919 IM_WORK_T src = inp->channel[ch] * src_alpha;
920 IM_WORK_T orig = outp->channel[ch] * orig_alpha;
921 IM_WORK_T src_da = src * orig_alpha;
922 IM_WORK_T dest_sa = orig * src_alpha;
923 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
924 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
926 outp->channel[color_channels] = dest_alpha;
933 while (work_count--) {
934 IM_WORK_T src_alpha = inp->channel[color_channels];
936 for (ch = 0; ch < color_channels; ++ch) {
937 IM_WORK_T src = inp->channel[ch] * src_alpha;
938 IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
939 IM_WORK_T src_da = src * IM_SAMPLE_MAX;
940 IM_WORK_T dest_sa = orig * src_alpha;
941 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
942 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
954 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
955 Da' = Sa + Da - Sa.Da
959 Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
960 = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
964 Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
965 = min(Sca, Dc.Sa) + Dc(1-Sa)
966 = Sa.min(Sc, Dc) + Dc - Dc.Sa
971 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
974 IM_COLOR const *inp = in;
975 IM_COLOR *outp = out;
976 int work_count = count;
977 int color_channels = i_color_channels(channels);
979 if (i_has_alpha(channels)) {
980 while (work_count--) {
981 IM_WORK_T src_alpha = inp->channel[color_channels];
984 IM_WORK_T orig_alpha = outp->channel[color_channels];
985 IM_WORK_T dest_alpha = src_alpha + orig_alpha
986 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
987 for (ch = 0; ch < color_channels; ++ch) {
988 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
989 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
990 IM_WORK_T ScaDa = Sca * orig_alpha;
991 IM_WORK_T DcaSa = Dca * src_alpha;
992 IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
995 minc + (Sca + Dca) * IM_SAMPLE_MAX
997 ) / (IM_SAMPLE_MAX * dest_alpha);
999 outp->channel[color_channels] = dest_alpha;
1006 while (work_count--) {
1007 IM_WORK_T src_alpha = inp->channel[color_channels];
1010 for (ch = 0; ch < color_channels; ++ch) {
1011 IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1012 ? outp->channel[ch] : inp->channel[ch];
1016 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1027 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1029 IM_COLOR const *inp = in;
1030 IM_COLOR *outp = out;
1031 int work_count = count;
1032 int color_channels = i_color_channels(channels);
1034 if (i_has_alpha(channels)) {
1035 while (work_count--) {
1036 IM_WORK_T src_alpha = inp->channel[color_channels];
1039 IM_WORK_T orig_alpha = outp->channel[color_channels];
1040 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1041 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1042 for (ch = 0; ch < color_channels; ++ch) {
1043 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1044 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1045 IM_WORK_T ScaDa = Sca * orig_alpha;
1046 IM_WORK_T DcaSa = Dca * src_alpha;
1047 IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1050 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1052 ) / (IM_SAMPLE_MAX * dest_alpha);
1054 outp->channel[color_channels] = dest_alpha;
1061 while (work_count--) {
1062 IM_WORK_T src_alpha = inp->channel[color_channels];
1065 for (ch = 0; ch < color_channels; ++ch) {
1066 IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1067 ? outp->channel[ch] : inp->channel[ch];
1071 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1082 #define IM_RGB_TO_HSV i_rgb_to_hsv
1083 #define IM_HSV_TO_RGB i_hsv_to_rgb
1085 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1086 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1090 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1093 IM_COLOR const *outp = out;
1094 int work_count = count;
1096 if (i_has_alpha(channels)) {
1097 while (work_count--) {
1100 /* only transfer hue if there's saturation */
1101 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1104 /* and no point in setting the target hue if the target has no sat */
1105 if (inp->channel[1]) {
1106 inp->channel[0] = c.channel[0];
1108 inp->channel[3] = c.channel[3];
1111 inp->channel[3] = 0;
1115 inp->channel[3] = 0;
1123 while (work_count--) {
1126 /* only transfer hue if there's saturation */
1127 if (c.channel[1] && inp->channel[3]) {
1130 /* and no point in setting the target hue if the target has no sat */
1131 if (inp->channel[1]) {
1132 inp->channel[0] = c.channel[0];
1134 inp->channel[3] = c.channel[3];
1138 inp->channel[3] = 0;
1146 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1151 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1154 IM_COLOR const *outp = out;
1155 int work_count = count;
1157 while (work_count--) {
1162 inp->channel[1] = c.channel[1];
1164 inp->channel[3] = c.channel[3];
1169 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1174 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1177 IM_COLOR const *outp = out;
1178 int work_count = count;
1180 while (work_count--) {
1185 inp->channel[2] = c.channel[2];
1187 inp->channel[3] = c.channel[3];
1193 /* all images have a "value channel" - for greyscale it's the only
1195 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1199 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1202 IM_COLOR const *outp = out;
1203 int work_count = count;
1205 while (work_count--) {
1210 inp->channel[0] = c.channel[0];
1211 inp->channel[1] = c.channel[1];
1213 inp->channel[3] = c.channel[3];
1218 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1222 #undef IM_RGB_TO_HSV
1223 #undef IM_HSV_TO_RGB