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));
357 dump_src(const char *note, unsigned char const *src, i_img_dim width) {
359 printf("%s - %p/%d\n", note, src, width);
360 for (i = 0; i < width; ++i) {
361 printf("%02x ", src[i]);
369 =item i_render_line(r, x, y, width, source, fill)
372 Render the given fill with the coverage in C<source[0]> through
377 =item i_render_linef(r, x, y, width, source, fill)
380 Render the given fill with the coverage in C<source[0]> through
387 IM_RENDER_LINE(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
388 const IM_SAMPLE_T *src, IM_COLOR *line,
389 IM_FILL_COMBINE_F combine) {
391 int src_chans = im->channels;
393 /* src must always have an alpha channel */
394 if (src_chans == 1 || src_chans == 3)
397 if (y < 0 || y >= im->ysize)
405 if (x + width > im->xsize)
406 width = r->im->xsize - x;
409 alloc_line(r, width, 1);
411 alloc_line(r, width, 0);
416 i_img_dim work_width = width;
417 IM_COLOR *linep = line;
418 const IM_SAMPLE_T *srcp = src;
419 int alpha_chan = src_chans - 1;
423 if (*srcp != IM_SAMPLE_MAX)
424 linep->channel[alpha_chan] =
425 linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
428 linep->channel[alpha_chan] = 0;
435 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
436 combine(r->IM_SUFFIX(line), line, im->channels, width);
437 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
441 i_img_dim work_width = width;
442 IM_COLOR *srcc = line;
443 IM_COLOR *destc = r->IM_SUFFIX(line);
445 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
448 /* just replace it */
453 for (ch = 0; ch < im->channels; ++ch) {
454 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
455 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
456 destc->channel[ch] = IM_LIMIT(work);
465 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
468 IM_PLIN(im, x, x+width, y, line);
475 IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y,
476 i_img_dim width, unsigned char const *src,
477 i_color const *color) {
479 IM_COLOR *linep = r->IM_SUFFIX(line);
480 int ch, channels = im->channels;
481 i_img_dim fetch_offset;
484 #define STORE_COLOR (*color)
488 for (ch = 0; ch < channels; ++ch) {
489 fcolor.channel[ch] = color->channel[ch] / 255.0;
491 #define STORE_COLOR fcolor
495 while (fetch_offset < width && *src == 0xFF) {
496 *linep++ = STORE_COLOR;
500 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
501 while (fetch_offset < width) {
503 IM_WORK_T alpha = *src++;
505 IM_WORK_T alpha = *src++ / 255.0;
507 if (alpha == IM_SAMPLE_MAX)
508 *linep = STORE_COLOR;
510 for (ch = 0; ch < channels; ++ch) {
511 linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
512 + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
518 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
523 IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
524 i_img_dim width, unsigned char const *src,
525 i_color const *color) {
526 IM_COLOR *linep = r->IM_SUFFIX(line);
528 int alpha_channel = r->im->channels - 1;
529 i_img_dim fetch_offset;
532 #define STORE_COLOR (*color)
536 for (ch = 0; ch < r->im->channels; ++ch) {
537 fcolor.channel[ch] = color->channel[ch] / 255.0;
539 #define STORE_COLOR fcolor
543 while (fetch_offset < width && *src == 0xFF) {
544 *linep++ = STORE_COLOR;
548 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
549 while (fetch_offset < width) {
551 IM_WORK_T src_alpha = *src++;
553 IM_WORK_T src_alpha = *src++ / 255.0;
555 if (src_alpha == IM_SAMPLE_MAX)
556 *linep = STORE_COLOR;
557 else if (src_alpha) {
558 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
559 IM_WORK_T orig_alpha = linep->channel[alpha_channel];
560 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
561 for (ch = 0; ch < alpha_channel; ++ch) {
562 linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
563 + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
566 linep->channel[alpha_channel] = dest_alpha;
571 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
575 /* combine a line of image data with an output line, both the input
576 and output lines include an alpha channel.
578 Both input and output lines have I<channels> of data, channels
579 should be either 2 or 4.
583 IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
584 int channels, i_img_dim count) {
586 int alpha_channel = channels - 1;
589 IM_WORK_T src_alpha = in->channel[alpha_channel];
591 if (src_alpha == IM_SAMPLE_MAX)
593 else if (src_alpha) {
594 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
595 IM_WORK_T orig_alpha = out->channel[alpha_channel];
596 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
598 for (ch = 0; ch < alpha_channel; ++ch) {
599 out->channel[ch] = ( src_alpha * in->channel[ch]
600 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
603 out->channel[alpha_channel] = dest_alpha;
612 /* combine a line of image data with an output line. The input line
613 includes an alpha channel, the output line has no alpha channel.
615 The input line has I<channels>+1 of color data. The output line
616 has I<channels> of color data.
620 IM_SUFFIX(combine_line_noalpha)
621 (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
625 IM_WORK_T src_alpha = in->channel[channels];
627 if (src_alpha == IM_SAMPLE_MAX)
629 else if (src_alpha) {
632 remains = IM_SAMPLE_MAX - src_alpha;
633 for (ch = 0; ch < channels; ++ch) {
634 out->channel[ch] = ( in->channel[ch] * src_alpha
635 + out->channel[ch] * remains) / IM_SAMPLE_MAX;
645 /* combine a line of image data with an output line, both the input
646 and output lines include an alpha channel.
648 Both input and output lines have I<channels> of data, channels
649 should be either 2 or 4.
651 This variant does not modify the output alpha channel.
655 IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
656 int channels, i_img_dim count) {
658 int alpha_channel = channels - 1;
661 IM_WORK_T src_alpha = in->channel[alpha_channel];
663 if (src_alpha == IM_SAMPLE_MAX)
665 else if (src_alpha) {
666 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
667 IM_WORK_T orig_alpha = out->channel[alpha_channel];
668 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
670 for (ch = 0; ch < alpha_channel; ++ch) {
671 out->channel[ch] = ( src_alpha * in->channel[ch]
672 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
684 IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
685 if (channels == 2 || channels == 4)
686 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
688 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
692 IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
693 if (channels == 2 || channels == 4)
694 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
696 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
699 static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
700 static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
701 static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
702 static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
703 static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
704 static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
705 static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
706 static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
707 static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
708 static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
709 static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
710 static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
712 static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
715 IM_SUFFIX(combine_alphablend),
716 IM_SUFFIX(combine_mult),
717 IM_SUFFIX(combine_dissolve),
718 IM_SUFFIX(combine_add),
719 IM_SUFFIX(combine_subtract),
720 IM_SUFFIX(combine_diff),
721 IM_SUFFIX(combine_lighten),
722 IM_SUFFIX(combine_darken),
723 IM_SUFFIX(combine_hue),
724 IM_SUFFIX(combine_sat),
725 IM_SUFFIX(combine_value),
726 IM_SUFFIX(combine_color)
732 =item i_get_combine(combine, color_func, fcolor_func)
737 void i_get_combine(int combine, i_fill_combine_f *color_func,
738 i_fill_combinef_f *fcolor_func) {
739 if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
742 *color_func = combines_8[combine];
743 *fcolor_func = combines_double[combine];
749 Three good references for implementing combining modes:
751 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
752 referenced as [svg1.2]
754 http://gimp-savvy.com/BOOK/index.html?node55.html
755 ("The Blending Modes", if it changes)
756 referenced as [savvy]
758 http://www.pegtop.net/delphi/articles/blendmodes/
759 referenced as [pegtop]
761 Where differences exist, I follow the SVG practice, the gimp
762 practice, and lastly pegtop.
767 IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
768 IM_SUFFIX(combine_line)(out, in, channels, count);
772 Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
773 Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
778 Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
781 IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
784 IM_COLOR *outp = out;
785 i_img_dim work_count = count;
786 int color_channels = i_color_channels(channels);
788 if (i_has_alpha(channels)) {
789 while (work_count--) {
790 IM_WORK_T orig_alpha = outp->channel[color_channels];
791 IM_WORK_T src_alpha = inp->channel[color_channels];
794 IM_WORK_T dest_alpha = src_alpha + orig_alpha
795 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
797 for (ch = 0; ch < color_channels; ++ch) {
799 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
801 + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
802 + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
803 / IM_SAMPLE_MAX / dest_alpha;
805 outp->channel[color_channels] = dest_alpha;
812 while (work_count--) {
813 IM_WORK_T src_alpha = inp->channel[color_channels];
814 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
817 for (ch = 0; ch < color_channels; ++ch) {
819 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
820 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
830 IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
831 int color_channels = i_color_channels(channels);
834 if (i_has_alpha(channels)) {
836 if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
837 for (ch = 0; ch < color_channels; ++ch) {
838 out->channel[ch] = in->channel[ch];
840 out->channel[color_channels] = IM_SAMPLE_MAX;
848 if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
849 for (ch = 0; ch < color_channels; ++ch) {
850 out->channel[ch] = in->channel[ch];
860 Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
862 Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
867 IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
869 int color_channels = i_color_channels(channels);
870 i_img_dim work_count = count;
872 IM_COLOR *outp = out;
874 if (i_has_alpha(channels)) {
875 while (work_count--) {
876 IM_WORK_T src_alpha = inp->channel[color_channels];
878 IM_WORK_T orig_alpha = outp->channel[color_channels];
879 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
880 if (dest_alpha > IM_SAMPLE_MAX)
881 dest_alpha = IM_SAMPLE_MAX;
882 for (ch = 0; ch < color_channels; ++ch) {
883 IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
884 if (total > IM_SAMPLE_MAX)
885 total = IM_SAMPLE_MAX;
886 outp->channel[ch] = total;
888 outp->channel[color_channels] = dest_alpha;
896 while (work_count--) {
897 IM_WORK_T src_alpha = inp->channel[color_channels];
899 for (ch = 0; ch < color_channels; ++ch) {
900 IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
901 if (total > IM_SAMPLE_MAX)
902 total = IM_SAMPLE_MAX;
903 outp->channel[ch] = total;
914 [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
915 it as max(A-B, 0). [svg1.2] doesn't cover it.
917 [savvy] doesn't document how it works with an alpha channel. GIMP
918 actually seems to calculate the final value then use the alpha
919 channel to apply that to the target.
922 IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
924 IM_COLOR const *inp = in;
925 IM_COLOR *outp = out;
926 i_img_dim work_count = count;
927 int color_channels = i_color_channels(channels);
929 if (i_has_alpha(channels)) {
930 while (work_count--) {
931 IM_WORK_T src_alpha = inp->channel[color_channels];
933 IM_WORK_T orig_alpha = outp->channel[color_channels];
934 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
935 if (dest_alpha > IM_SAMPLE_MAX)
936 dest_alpha = IM_SAMPLE_MAX;
937 for (ch = 0; ch < color_channels; ++ch) {
939 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
943 outp->channel[ch] = total;
945 outp->channel[color_channels] = dest_alpha;
952 while (work_count--) {
953 IM_WORK_T src_alpha = inp->channel[color_channels];
955 for (ch = 0; ch < color_channels; ++ch) {
956 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
959 outp->channel[ch] = total;
969 #define IM_abs(x) abs(x)
971 #define IM_abs(x) fabs(x)
975 Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
976 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
977 Da' = Sa + Da - Sa.Da
980 IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
982 IM_COLOR const *inp = in;
983 IM_COLOR *outp = out;
984 i_img_dim work_count = count;
985 int color_channels = i_color_channels(channels);
987 if (i_has_alpha(channels)) {
988 while (work_count--) {
989 IM_WORK_T src_alpha = inp->channel[color_channels];
991 IM_WORK_T orig_alpha = outp->channel[color_channels];
992 IM_WORK_T dest_alpha = src_alpha + orig_alpha
993 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
994 for (ch = 0; ch < color_channels; ++ch) {
995 IM_WORK_T src = inp->channel[ch] * src_alpha;
996 IM_WORK_T orig = outp->channel[ch] * orig_alpha;
997 IM_WORK_T src_da = src * orig_alpha;
998 IM_WORK_T dest_sa = orig * src_alpha;
999 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1000 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1002 outp->channel[color_channels] = dest_alpha;
1009 while (work_count--) {
1010 IM_WORK_T src_alpha = inp->channel[color_channels];
1012 for (ch = 0; ch < color_channels; ++ch) {
1013 IM_WORK_T src = inp->channel[ch] * src_alpha;
1014 IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1015 IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1016 IM_WORK_T dest_sa = orig * src_alpha;
1017 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1018 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1030 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1031 Da' = Sa + Da - Sa.Da
1035 Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1036 = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1040 Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1041 = min(Sca, Dc.Sa) + Dc(1-Sa)
1042 = Sa.min(Sc, Dc) + Dc - Dc.Sa
1047 IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
1050 IM_COLOR const *inp = in;
1051 IM_COLOR *outp = out;
1052 i_img_dim work_count = count;
1053 int color_channels = i_color_channels(channels);
1055 if (i_has_alpha(channels)) {
1056 while (work_count--) {
1057 IM_WORK_T src_alpha = inp->channel[color_channels];
1060 IM_WORK_T orig_alpha = outp->channel[color_channels];
1061 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1062 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1063 for (ch = 0; ch < color_channels; ++ch) {
1064 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1065 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1066 IM_WORK_T ScaDa = Sca * orig_alpha;
1067 IM_WORK_T DcaSa = Dca * src_alpha;
1068 IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1071 minc + (Sca + Dca) * IM_SAMPLE_MAX
1073 ) / (IM_SAMPLE_MAX * dest_alpha);
1075 outp->channel[color_channels] = dest_alpha;
1082 while (work_count--) {
1083 IM_WORK_T src_alpha = inp->channel[color_channels];
1086 for (ch = 0; ch < color_channels; ++ch) {
1087 IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1088 ? outp->channel[ch] : inp->channel[ch];
1092 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1103 IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1105 IM_COLOR const *inp = in;
1106 IM_COLOR *outp = out;
1107 i_img_dim work_count = count;
1108 int color_channels = i_color_channels(channels);
1110 if (i_has_alpha(channels)) {
1111 while (work_count--) {
1112 IM_WORK_T src_alpha = inp->channel[color_channels];
1115 IM_WORK_T orig_alpha = outp->channel[color_channels];
1116 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1117 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1118 for (ch = 0; ch < color_channels; ++ch) {
1119 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1120 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1121 IM_WORK_T ScaDa = Sca * orig_alpha;
1122 IM_WORK_T DcaSa = Dca * src_alpha;
1123 IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1126 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1128 ) / (IM_SAMPLE_MAX * dest_alpha);
1130 outp->channel[color_channels] = dest_alpha;
1137 while (work_count--) {
1138 IM_WORK_T src_alpha = inp->channel[color_channels];
1141 for (ch = 0; ch < color_channels; ++ch) {
1142 IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1143 ? outp->channel[ch] : inp->channel[ch];
1147 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1158 #define IM_RGB_TO_HSV i_rgb_to_hsv
1159 #define IM_HSV_TO_RGB i_hsv_to_rgb
1161 #define IM_RGB_TO_HSV i_rgb_to_hsvf
1162 #define IM_HSV_TO_RGB i_hsv_to_rgbf
1166 IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1169 IM_COLOR const *outp = out;
1170 i_img_dim work_count = count;
1172 if (i_has_alpha(channels)) {
1173 while (work_count--) {
1176 /* only transfer hue if there's saturation */
1177 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1180 /* and no point in setting the target hue if the target has no sat */
1181 if (inp->channel[1]) {
1182 inp->channel[0] = c.channel[0];
1184 inp->channel[3] = c.channel[3];
1187 inp->channel[3] = 0;
1191 inp->channel[3] = 0;
1199 while (work_count--) {
1202 /* only transfer hue if there's saturation */
1203 if (c.channel[1] && inp->channel[3]) {
1206 /* and no point in setting the target hue if the target has no sat */
1207 if (inp->channel[1]) {
1208 inp->channel[0] = c.channel[0];
1210 inp->channel[3] = c.channel[3];
1214 inp->channel[3] = 0;
1222 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1227 IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1230 IM_COLOR const *outp = out;
1231 i_img_dim work_count = count;
1233 while (work_count--) {
1238 inp->channel[1] = c.channel[1];
1240 inp->channel[3] = c.channel[3];
1245 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1250 IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1253 IM_COLOR const *outp = out;
1254 i_img_dim work_count = count;
1256 while (work_count--) {
1261 inp->channel[2] = c.channel[2];
1263 inp->channel[3] = c.channel[3];
1269 /* all images have a "value channel" - for greyscale it's the only
1271 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1275 IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
1278 IM_COLOR const *outp = out;
1279 i_img_dim work_count = count;
1281 while (work_count--) {
1286 inp->channel[0] = c.channel[0];
1287 inp->channel[1] = c.channel[1];
1289 inp->channel[3] = c.channel[3];
1294 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1298 #undef IM_RGB_TO_HSV
1299 #undef IM_HSV_TO_RGB