add new comparison method rgb_difference that resembles arithmetical difference per...
[imager.git] / render.im
CommitLineData
9c106321
TC
1/*
2Render utilities
3*/
4#include "imager.h"
5
6#define RENDER_MAGIC 0x765AE
7
50c75381 8typedef void (*render_color_f)(i_render *, i_img_dim, i_img_dim, i_img_dim, unsigned char const *src, i_color const *color);
9c106321 9
9b1ec2b8
TC
10#define i_has_alpha(channels) ((channels) == 2 || (channels) == 4)
11
12#define i_color_channels(channels) (i_has_alpha(channels) ? (channels)-1 : (channels))
13
9c106321
TC
14#code
15
50c75381
TC
16static 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);
17static 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);
9c106321
TC
18
19static render_color_f IM_SUFFIX(render_color_tab)[] =
20 {
21 NULL,
22 IM_SUFFIX(render_color_13),
23 IM_SUFFIX(render_color_alpha),
24 IM_SUFFIX(render_color_13),
25 IM_SUFFIX(render_color_alpha),
26 };
27
50c75381
TC
28static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
29static void IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
9b1ec2b8 30/* the copy variant copies the source alpha to the the output alpha channel */
50c75381 31static void IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
9b1ec2b8 32
50c75381
TC
33static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
34static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
9b1ec2b8 35
9c106321
TC
36#/code
37
50c75381
TC
38/*
39=item i_render_new(im, width)
40=category Blit tools
41
42Allocate a new C<i_render> object and initialize it.
43
44=cut
45*/
46
47i_render *
48i_render_new(i_img *im, i_img_dim width) {
49 i_render *r = mymalloc(sizeof(i_render));
50
51 i_render_init(r, im, width);
52
53 return r;
54}
55
56/*
57=item i_render_delete(r)
58=category Blit tools
59
60Release an C<i_render> object.
61
62=cut
63*/
64
9c106321 65void
50c75381
TC
66i_render_delete(i_render *r) {
67 i_render_done(r);
68 myfree(r);
69}
70
71void
72i_render_init(i_render *r, i_img *im, i_img_dim width) {
9c106321
TC
73 r->magic = RENDER_MAGIC;
74 r->im = im;
9b1ec2b8 75 r->line_width = width;
9c106321
TC
76 r->line_8 = NULL;
77 r->line_double = NULL;
9b1ec2b8
TC
78 r->fill_width = width;
79 r->fill_line_8 = NULL;
80 r->fill_line_double = NULL;
9c106321
TC
81}
82
83void
84i_render_done(i_render *r) {
85 if (r->line_8)
86 myfree(r->line_8);
9b1ec2b8 87 if (r->line_double)
9c106321 88 myfree(r->line_double);
9b1ec2b8
TC
89 if (r->fill_line_8)
90 myfree(r->fill_line_8);
91 if (r->fill_line_double)
92 myfree(r->fill_line_double);
9c106321
TC
93 r->magic = 0;
94}
95
9b1ec2b8 96static void
50c75381 97alloc_line(i_render *r, i_img_dim width, i_img_dim eight_bit) {
9b1ec2b8 98 if (width > r->line_width) {
50c75381 99 i_img_dim new_width = r->line_width * 2;
9b1ec2b8
TC
100 if (new_width < width)
101 new_width = width;
102
103 if (eight_bit) {
104 if (r->line_8)
105 r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
106 else
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;
111 }
112 }
113 else {
114 if (r->line_double)
115 r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
116 else
117 r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
118 if (r->line_8) {
119 myfree(r->line_8);
120 r->line_double = NULL;
121 }
122 }
123
124 r->line_width = new_width;
125 }
126 else {
127 if (eight_bit) {
128 if (!r->line_8)
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;
133 }
134 }
135 else {
136 if (!r->line_double)
137 r->line_double = mymalloc(sizeof(i_fcolor) * r->line_width);
138 if (r->line_8) {
139 myfree(r->line_8);
140 r->line_8 = NULL;
141 }
142 }
143 }
144}
145
146static void
50c75381 147alloc_fill_line(i_render *r, i_img_dim width, int eight_bit) {
9b1ec2b8 148 if (width > r->fill_width) {
50c75381 149 i_img_dim new_width = r->fill_width * 2;
9b1ec2b8
TC
150 if (new_width < width)
151 new_width = width;
152
153 if (eight_bit) {
154 if (r->line_8)
155 r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
156 else
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;
161 }
162 }
163 else {
164 if (r->fill_line_double)
165 r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
166 else
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;
171 }
172 }
173
174 r->fill_width = new_width;
175 }
176 else {
177 if (eight_bit) {
178 if (!r->fill_line_8)
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;
183 }
184 }
185 else {
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;
191 }
192 }
193 }
194}
195
50c75381
TC
196/*
197=item i_render_color(r, x, y, width, source, color)
198=category Blit tools
199
200Render the given color with the coverage specified by C<source[0]> to
201C<source[width-1]>.
202
203Renders in normal combine mode.
204
205=cut
206*/
207
9c106321 208void
50c75381
TC
209i_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) {
9c106321
TC
211 i_img *im = r->im;
212 if (y < 0 || y >= im->ysize)
213 return;
214 if (x < 0) {
215 width += x;
216 src -= x;
217 x = 0;
218 }
219 if (x + width > im->xsize) {
220 width = im->xsize - x;
221 }
222 if (x >= im->xsize || x + width <= 0 || width <= 0)
223 return;
224
225 /* avoid as much work as we can */
226 while (width > 0 && *src == 0) {
227 --width;
228 ++src;
229 ++x;
230 }
231 while (width > 0 && src[width-1] == 0) {
232 --width;
233 }
234 if (!width)
235 return;
236
9b1ec2b8 237 alloc_line(r, width, r->im->bits <= 8);
9b1ec2b8
TC
238
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);
243#/code
244}
245
50c75381
TC
246/*
247=item i_render_fill(r, x, y, width, source, fill)
248=category Blit tools
249
250Render the given fill with the coverage in C<source[0]> through
251C<source[width-1]>.
252
253=cut
254*/
255
9b1ec2b8 256void
50c75381
TC
257i_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) {
9b1ec2b8
TC
259 i_img *im = r->im;
260 int fill_channels = im->channels;
261
262 if (fill_channels == 1 || fill_channels == 3)
263 ++fill_channels;
264
265 if (y < 0 || y >= im->ysize)
266 return;
267 if (x < 0) {
268 width += x;
269 src -= x;
270 x = 0;
271 }
272 if (x + width > im->xsize) {
273 width = im->xsize - x;
274 }
275 if (x >= im->xsize || x + width <= 0 || width <= 0)
276 return;
277
278 if (src) {
279 /* avoid as much work as we can */
280 while (width > 0 && *src == 0) {
281 --width;
282 ++src;
283 ++x;
284 }
285 while (width > 0 && src[width-1] == 0) {
286 --width;
287 }
288 }
289 if (!width)
290 return;
291
9b1ec2b8
TC
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);
9c106321 294
9b1ec2b8
TC
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));
300 if (src) {
301 unsigned char const *srcc = src;
302 IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
50c75381 303 i_img_dim work_width = width;
9b1ec2b8
TC
304 while (work_width) {
305 if (*srcc == 0) {
306 fillc->channel[fill_channels-1] = 0;
307 }
308 else if (*srcc != 255) {
309 fillc->channel[fill_channels-1] =
310 fillc->channel[fill_channels-1] * *srcc / 255;
311 }
312 --work_width;
313 ++srcc;
314 ++fillc;
315 }
316 }
317 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
318 IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
319 }
320 else {
321 if (src) {
50c75381 322 i_img_dim work_width = width;
9b1ec2b8
TC
323 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
324 IM_COLOR *destc = r->IM_SUFFIX(line);
325 int ch;
326
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));
329 while (work_width) {
330 if (*src == 255) {
331 /* just replace it */
332 *destc = *srcc;
333 }
334 else if (*src) {
335 for (ch = 0; ch < im->channels; ++ch) {
7a98c442
TC
336 IM_WORK_T work = (destc->channel[ch] * (255 - *src)
337 + srcc->channel[ch] * *src) / 255.0;
9b1ec2b8
TC
338 destc->channel[ch] = IM_LIMIT(work);
339 }
340 }
341
342 ++srcc;
343 ++destc;
344 ++src;
345 --work_width;
346 }
347 }
348 else { /* if (src) */
e958b64e 349 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(line));
9b1ec2b8
TC
350 }
351 }
352 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
9c106321
TC
353#/code
354}
355
8d14daab
TC
356#if 0
357
358/* for debuggin */
359
9c106321 360static void
50c75381
TC
361dump_src(const char *note, unsigned char const *src, i_img_dim width) {
362 i_img_dim i;
8d14daab 363 printf("%s - %p/%" i_DF "\n", note, src, i_DFc(width));
9c106321
TC
364 for (i = 0; i < width; ++i) {
365 printf("%02x ", src[i]);
366 }
367 putchar('\n');
368}
369
8d14daab
TC
370#endif
371
9c106321
TC
372#code
373
50c75381
TC
374/*
375=item i_render_line(r, x, y, width, source, fill)
376=category Blit tools
377
378Render the given fill with the coverage in C<source[0]> through
379C<source[width-1]>.
380
381=cut
382
383=item i_render_linef(r, x, y, width, source, fill)
384=category Blit tools
385
386Render the given fill with the coverage in C<source[0]> through
387C<source[width-1]>.
388
389=cut
390*/
391
9b1ec2b8 392void
50c75381
TC
393IM_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) {
9b1ec2b8
TC
396 i_img *im = r->im;
397 int src_chans = im->channels;
398
399 /* src must always have an alpha channel */
400 if (src_chans == 1 || src_chans == 3)
401 ++src_chans;
402
403 if (y < 0 || y >= im->ysize)
404 return;
405 if (x < 0) {
406 src -= x;
407 line -= x;
408 width += x;
409 x = 0;
410 }
411 if (x + width > im->xsize)
412 width = r->im->xsize - x;
413
414#ifdef IM_EIGHT_BIT
415 alloc_line(r, width, 1);
416#else
417 alloc_line(r, width, 0);
418#endif
419
420 if (combine) {
421 if (src) {
50c75381 422 i_img_dim work_width = width;
9b1ec2b8
TC
423 IM_COLOR *linep = line;
424 const IM_SAMPLE_T *srcp = src;
425 int alpha_chan = src_chans - 1;
426
427 while (work_width) {
428 if (*srcp) {
429 if (*srcp != IM_SAMPLE_MAX)
430 linep->channel[alpha_chan] =
431 linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
432 }
433 else {
434 linep->channel[alpha_chan] = 0;
435 }
436 --work_width;
437 ++srcp;
438 ++linep;
439 }
440 }
441 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
442 combine(r->IM_SUFFIX(line), line, im->channels, width);
443 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
444 }
445 else {
446 if (src) {
50c75381 447 i_img_dim work_width = width;
9b1ec2b8
TC
448 IM_COLOR *srcc = line;
449 IM_COLOR *destc = r->IM_SUFFIX(line);
450
451 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
452 while (work_width) {
453 if (*src == 255) {
454 /* just replace it */
455 *destc = *srcc;
456 }
457 else if (*src) {
458 int ch;
459 for (ch = 0; ch < im->channels; ++ch) {
460 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
461 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
462 destc->channel[ch] = IM_LIMIT(work);
463 }
464 }
465
466 ++srcc;
467 ++destc;
468 ++src;
469 --work_width;
470 }
471 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
472 }
473 else {
474 IM_PLIN(im, x, x+width, y, line);
475 }
476 }
477}
478
9c106321
TC
479static
480void
50c75381
TC
481IM_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) {
9c106321
TC
484 i_img *im = r->im;
485 IM_COLOR *linep = r->IM_SUFFIX(line);
486 int ch, channels = im->channels;
50c75381 487 i_img_dim fetch_offset;
d8d215e3 488 int color_alpha = color->channel[im->channels];
9c106321
TC
489#undef STORE_COLOR
490#ifdef IM_EIGHT_BIT
491#define STORE_COLOR (*color)
492#else
493 i_fcolor fcolor;
494
495 for (ch = 0; ch < channels; ++ch) {
496 fcolor.channel[ch] = color->channel[ch] / 255.0;
497 }
498#define STORE_COLOR fcolor
499#endif
500
501 fetch_offset = 0;
d8d215e3
TC
502 if (color_alpha == 0xFF) {
503 while (fetch_offset < width && *src == 0xFF) {
504 *linep++ = STORE_COLOR;
505 ++src;
506 ++fetch_offset;
507 }
9c106321
TC
508 }
509 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
510 while (fetch_offset < width) {
511#ifdef IM_EIGHT_BIT
d8d215e3 512 IM_WORK_T alpha = *src++ * color_alpha / 255;
9c106321 513#else
d8d215e3 514 IM_WORK_T alpha = *src++ * color_alpha / (255.0 * 255.0);
9c106321
TC
515#endif
516 if (alpha == IM_SAMPLE_MAX)
517 *linep = STORE_COLOR;
518 else if (alpha) {
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;
522 }
523 }
524 ++linep;
525 ++fetch_offset;
526 }
527 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
528}
529
530static
531void
50c75381
TC
532IM_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) {
9c106321
TC
535 IM_COLOR *linep = r->IM_SUFFIX(line);
536 int ch;
537 int alpha_channel = r->im->channels - 1;
50c75381 538 i_img_dim fetch_offset;
d8d215e3 539 int color_alpha = color->channel[alpha_channel];
9c106321
TC
540#undef STORE_COLOR
541#ifdef IM_EIGHT_BIT
542#define STORE_COLOR (*color)
543#else
544 i_fcolor fcolor;
545
546 for (ch = 0; ch < r->im->channels; ++ch) {
547 fcolor.channel[ch] = color->channel[ch] / 255.0;
548 }
549#define STORE_COLOR fcolor
550#endif
551
552 fetch_offset = 0;
d8d215e3
TC
553 if (color->channel[alpha_channel] == 0xFF) {
554 while (fetch_offset < width && *src == 0xFF) {
555 *linep++ = STORE_COLOR;
556 ++src;
557 ++fetch_offset;
558 }
9c106321
TC
559 }
560 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
561 while (fetch_offset < width) {
562#ifdef IM_EIGHT_BIT
d8d215e3 563 IM_WORK_T src_alpha = *src++ * color_alpha / 255;
9c106321 564#else
d8d215e3 565 IM_WORK_T src_alpha = *src++ * color_alpha / (255.0 * 255.0);
9c106321
TC
566#endif
567 if (src_alpha == IM_SAMPLE_MAX)
568 *linep = STORE_COLOR;
569 else if (src_alpha) {
e6e94ab0 570 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
9c106321
TC
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
576 ) / dest_alpha;
577 }
578 linep->channel[alpha_channel] = dest_alpha;
579 }
580 ++linep;
581 ++fetch_offset;
582 }
583 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
9b1ec2b8
TC
584#undef STORE_COLOR
585}
586
587/* combine a line of image data with an output line, both the input
588 and output lines include an alpha channel.
589
590 Both input and output lines have I<channels> of data, channels
591 should be either 2 or 4.
592*/
593
594static void
595IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
50c75381 596 int channels, i_img_dim count) {
9b1ec2b8
TC
597 int ch;
598 int alpha_channel = channels - 1;
599
600 while (count) {
601 IM_WORK_T src_alpha = in->channel[alpha_channel];
602
603 if (src_alpha == IM_SAMPLE_MAX)
604 *out = *in;
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;
609
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
613 ) / dest_alpha;
614 }
615 out->channel[alpha_channel] = dest_alpha;
616 }
617
618 ++out;
619 ++in;
620 --count;
621 }
622}
623
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.
626
627 The input line has I<channels>+1 of color data. The output line
628 has I<channels> of color data.
629*/
630
631static void
632IM_SUFFIX(combine_line_noalpha)
50c75381 633 (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
9b1ec2b8
TC
634 int ch;
635
636 while (count) {
637 IM_WORK_T src_alpha = in->channel[channels];
638
639 if (src_alpha == IM_SAMPLE_MAX)
640 *out = *in;
641 else if (src_alpha) {
642 IM_WORK_T remains;
643
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;
648 }
649 }
650
651 ++out;
652 ++in;
653 --count;
654 }
655}
656
657/* combine a line of image data with an output line, both the input
658 and output lines include an alpha channel.
659
660 Both input and output lines have I<channels> of data, channels
661 should be either 2 or 4.
662
663 This variant does not modify the output alpha channel.
664*/
665
666static void
667IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
50c75381 668 int channels, i_img_dim count) {
9b1ec2b8
TC
669 int ch;
670 int alpha_channel = channels - 1;
671
672 while (count) {
673 IM_WORK_T src_alpha = in->channel[alpha_channel];
674
675 if (src_alpha == IM_SAMPLE_MAX)
676 *out = *in;
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;
681
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
685 ) / dest_alpha;
686 }
687 }
688
689 ++out;
690 ++in;
691 --count;
692 }
693}
694
695static void
50c75381 696IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
9b1ec2b8
TC
697 if (channels == 2 || channels == 4)
698 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
699 else
700 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
701}
702
703static void
50c75381 704IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
9b1ec2b8
TC
705 if (channels == 2 || channels == 4)
706 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
707 else
708 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
709}
710
50c75381
TC
711static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
712static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
713static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
714static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
715static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
716static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
717static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
718static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
719static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
720static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
721static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
722static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
9b1ec2b8
TC
723
724static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
725{
726 NULL,
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)
739};
740
741#/code
742
743/*
744=item i_get_combine(combine, color_func, fcolor_func)
745
746=cut
747*/
748
749void i_get_combine(int combine, i_fill_combine_f *color_func,
750 i_fill_combinef_f *fcolor_func) {
c1c8b1e9 751 if (combine < 0 || combine >= sizeof(combines_8) / sizeof(*combines_8))
9b1ec2b8
TC
752 combine = 0;
753
754 *color_func = combines_8[combine];
755 *fcolor_func = combines_double[combine];
756}
757
758#code
759
760/*
761 Three good references for implementing combining modes:
762
763 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
764 referenced as [svg1.2]
765
766 http://gimp-savvy.com/BOOK/index.html?node55.html
767 ("The Blending Modes", if it changes)
768 referenced as [savvy]
769
770 http://www.pegtop.net/delphi/articles/blendmodes/
771 referenced as [pegtop]
772
773 Where differences exist, I follow the SVG practice, the gimp
774 practice, and lastly pegtop.
775*/
776
777
778static void
50c75381 779IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
780 IM_SUFFIX(combine_line)(out, in, channels, count);
781}
782
783/*
784Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
785Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
786 = Sa + Da - Sa.Da
787
788When Da=1
789
790Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
791 */
792static void
50c75381 793IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
794 int ch;
795 IM_COLOR *inp = in;
796 IM_COLOR *outp = out;
50c75381 797 i_img_dim work_count = count;
9b1ec2b8
TC
798 int color_channels = i_color_channels(channels);
799
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];
804
805 if (src_alpha) {
806 IM_WORK_T dest_alpha = src_alpha + orig_alpha
807 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
808
809 for (ch = 0; ch < color_channels; ++ch) {
810 outp->channel[ch] =
811 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
812 * orig_alpha
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;
816 }
817 outp->channel[color_channels] = dest_alpha;
818 }
819 ++outp;
820 ++inp;
821 }
822 }
823 else {
824 while (work_count--) {
825 IM_WORK_T src_alpha = inp->channel[color_channels];
826 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
827
828 if (src_alpha) {
829 for (ch = 0; ch < color_channels; ++ch) {
830 outp->channel[ch] =
831 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
832 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
833 }
834 }
835 ++outp;
836 ++inp;
837 }
838 }
839}
840
841static void
50c75381 842IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
843 int color_channels = i_color_channels(channels);
844 int ch;
845
846 if (i_has_alpha(channels)) {
847 while (count--) {
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];
851 }
852 out->channel[color_channels] = IM_SAMPLE_MAX;
853 }
854 ++out;
855 ++in;
856 }
857 }
858 else {
859 while (count--) {
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];
863 }
864 }
865 ++out;
866 ++in;
867 }
868 }
869}
870
871/*
872Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
873 = Sca + Dca
874Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
875 = Sa + Da
876*/
877
878static void
50c75381 879IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
880 int ch;
881 int color_channels = i_color_channels(channels);
50c75381 882 i_img_dim work_count = count;
9b1ec2b8
TC
883 IM_COLOR *inp = in;
884 IM_COLOR *outp = out;
885
886 if (i_has_alpha(channels)) {
887 while (work_count--) {
888 IM_WORK_T src_alpha = inp->channel[color_channels];
889 if (src_alpha) {
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;
899 }
900 outp->channel[color_channels] = dest_alpha;
901 }
902
903 ++outp;
904 ++inp;
905 }
906 }
907 else {
908 while (work_count--) {
909 IM_WORK_T src_alpha = inp->channel[color_channels];
910 if (src_alpha) {
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;
916 }
917 }
918
919 ++outp;
920 ++inp;
921 }
922 }
923}
924
925/*
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.
928
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.
932 */
933static void
50c75381 934IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
935 int ch;
936 IM_COLOR const *inp = in;
937 IM_COLOR *outp = out;
50c75381 938 i_img_dim work_count = count;
9b1ec2b8
TC
939 int color_channels = i_color_channels(channels);
940
941 if (i_has_alpha(channels)) {
942 while (work_count--) {
943 IM_WORK_T src_alpha = inp->channel[color_channels];
944 if (src_alpha) {
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) {
950 IM_WORK_T total =
951 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
952 / dest_alpha;
953 if (total < 0)
954 total = 0;
955 outp->channel[ch] = total;
956 }
957 outp->channel[color_channels] = dest_alpha;
958 }
959 ++outp;
960 ++inp;
961 }
962 }
963 else {
964 while (work_count--) {
965 IM_WORK_T src_alpha = inp->channel[color_channels];
966 if (src_alpha) {
967 for (ch = 0; ch < color_channels; ++ch) {
968 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
969 if (total < 0)
970 total = 0;
971 outp->channel[ch] = total;
972 }
973 }
974 ++outp;
975 ++inp;
976 }
977 }
978}
979
980#ifdef IM_EIGHT_BIT
981#define IM_abs(x) abs(x)
982#else
983#define IM_abs(x) fabs(x)
984#endif
985
986/*
987Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
988 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
989Da' = Sa + Da - Sa.Da
990*/
991static void
50c75381 992IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
993 int ch;
994 IM_COLOR const *inp = in;
995 IM_COLOR *outp = out;
50c75381 996 i_img_dim work_count = count;
9b1ec2b8
TC
997 int color_channels = i_color_channels(channels);
998
999 if (i_has_alpha(channels)) {
1000 while (work_count--) {
1001 IM_WORK_T src_alpha = inp->channel[color_channels];
1002 if (src_alpha) {
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;
1013 }
1014 outp->channel[color_channels] = dest_alpha;
1015 }
1016 ++inp;
1017 ++outp;
1018 }
1019 }
1020 else {
1021 while (work_count--) {
1022 IM_WORK_T src_alpha = inp->channel[color_channels];
1023 if (src_alpha) {
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;
1031 }
1032 }
1033 ++inp;
1034 ++outp;
1035 }
1036 }
1037}
1038
1039#undef IM_abs
1040
1041/*
1042 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1043 Da' = Sa + Da - Sa.Da
1044
1045 To hoist some code:
1046
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
1049
1050 When Da=1:
1051
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
1055 Da' = Sa + 1 - Sa.1
1056 = 1
1057 */
1058static void
1059IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
50c75381 1060 i_img_dim count) {
9b1ec2b8
TC
1061 int ch;
1062 IM_COLOR const *inp = in;
1063 IM_COLOR *outp = out;
50c75381 1064 i_img_dim work_count = count;
9b1ec2b8
TC
1065 int color_channels = i_color_channels(channels);
1066
1067 if (i_has_alpha(channels)) {
1068 while (work_count--) {
1069 IM_WORK_T src_alpha = inp->channel[color_channels];
1070
1071 if (src_alpha) {
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;
1081 outp->channel[ch] =
1082 (
1083 minc + (Sca + Dca) * IM_SAMPLE_MAX
1084 - ScaDa - DcaSa
1085 ) / (IM_SAMPLE_MAX * dest_alpha);
1086 }
1087 outp->channel[color_channels] = dest_alpha;
1088 }
1089 ++outp;
1090 ++inp;
1091 }
1092 }
1093 else {
1094 while (work_count--) {
1095 IM_WORK_T src_alpha = inp->channel[color_channels];
1096
1097 if (src_alpha) {
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];
1101 outp->channel[ch] =
1102 (
1103 src_alpha * minc +
1104 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1105 ) / IM_SAMPLE_MAX;
1106 }
1107 }
1108 ++outp;
1109 ++inp;
1110 }
1111 }
1112}
1113
1114static void
50c75381 1115IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1116 int ch;
1117 IM_COLOR const *inp = in;
1118 IM_COLOR *outp = out;
50c75381 1119 i_img_dim work_count = count;
9b1ec2b8
TC
1120 int color_channels = i_color_channels(channels);
1121
1122 if (i_has_alpha(channels)) {
1123 while (work_count--) {
1124 IM_WORK_T src_alpha = inp->channel[color_channels];
1125
1126 if (src_alpha) {
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;
1136 outp->channel[ch] =
1137 (
1138 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1139 - ScaDa - DcaSa
1140 ) / (IM_SAMPLE_MAX * dest_alpha);
1141 }
1142 outp->channel[color_channels] = dest_alpha;
1143 }
1144 ++outp;
1145 ++inp;
1146 }
1147 }
1148 else {
1149 while (work_count--) {
1150 IM_WORK_T src_alpha = inp->channel[color_channels];
1151
1152 if (src_alpha) {
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];
1156 outp->channel[ch] =
1157 (
1158 src_alpha * maxc +
1159 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1160 ) / IM_SAMPLE_MAX;
1161 }
1162 }
1163 ++outp;
1164 ++inp;
1165 }
1166 }
1167}
1168
1169#if IM_EIGHT_BIT
1170#define IM_RGB_TO_HSV i_rgb_to_hsv
1171#define IM_HSV_TO_RGB i_hsv_to_rgb
1172#else
1173#define IM_RGB_TO_HSV i_rgb_to_hsvf
1174#define IM_HSV_TO_RGB i_hsv_to_rgbf
1175#endif
1176
1177static void
50c75381 1178IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1179 if (channels > 2) {
1180 IM_COLOR *inp = in;
1181 IM_COLOR const *outp = out;
50c75381 1182 i_img_dim work_count = count;
9b1ec2b8
TC
1183
1184 if (i_has_alpha(channels)) {
1185 while (work_count--) {
1186 IM_COLOR c = *inp;
1187 IM_RGB_TO_HSV(&c);
1188 /* only transfer hue if there's saturation */
1189 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1190 *inp = *outp;
1191 IM_RGB_TO_HSV(inp);
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];
1195 IM_HSV_TO_RGB(inp);
1196 inp->channel[3] = c.channel[3];
1197 }
1198 else {
1199 inp->channel[3] = 0;
1200 }
1201 }
1202 else {
1203 inp->channel[3] = 0;
1204 }
1205
1206 ++outp;
1207 ++inp;
1208 }
1209 }
1210 else {
1211 while (work_count--) {
1212 IM_COLOR c = *inp;
1213 IM_RGB_TO_HSV(&c);
1214 /* only transfer hue if there's saturation */
1215 if (c.channel[1] && inp->channel[3]) {
1216 *inp = *outp;
1217 IM_RGB_TO_HSV(inp);
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];
1221 IM_HSV_TO_RGB(inp);
1222 inp->channel[3] = c.channel[3];
1223 }
1224 }
1225 else {
1226 inp->channel[3] = 0;
1227 }
1228
1229 ++outp;
1230 ++inp;
1231 }
1232 }
1233
1234 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1235 }
9c106321
TC
1236}
1237
9b1ec2b8 1238static void
50c75381 1239IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1240 if (channels > 2) {
1241 IM_COLOR *inp = in;
1242 IM_COLOR const *outp = out;
50c75381 1243 i_img_dim work_count = count;
9b1ec2b8
TC
1244
1245 while (work_count--) {
1246 IM_COLOR c = *inp;
1247 *inp = *outp;
1248 IM_RGB_TO_HSV(&c);
1249 IM_RGB_TO_HSV(inp);
1250 inp->channel[1] = c.channel[1];
1251 IM_HSV_TO_RGB(inp);
1252 inp->channel[3] = c.channel[3];
1253 ++outp;
1254 ++inp;
1255 }
1256
1257 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1258 }
1259}
1260
1261static void
50c75381 1262IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1263 if (channels > 2) {
1264 IM_COLOR *inp = in;
1265 IM_COLOR const *outp = out;
50c75381 1266 i_img_dim work_count = count;
9b1ec2b8
TC
1267
1268 while (work_count--) {
1269 IM_COLOR c = *inp;
1270 *inp = *outp;
1271 IM_RGB_TO_HSV(&c);
1272 IM_RGB_TO_HSV(inp);
1273 inp->channel[2] = c.channel[2];
1274 IM_HSV_TO_RGB(inp);
1275 inp->channel[3] = c.channel[3];
1276 ++outp;
1277 ++inp;
1278 }
1279 }
1280
1281 /* all images have a "value channel" - for greyscale it's the only
1282 colour channel */
1283 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1284}
1285
1286static void
50c75381 1287IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1288 if (channels > 2) {
1289 IM_COLOR *inp = in;
1290 IM_COLOR const *outp = out;
50c75381 1291 i_img_dim work_count = count;
9b1ec2b8
TC
1292
1293 while (work_count--) {
1294 IM_COLOR c = *inp;
1295 *inp = *outp;
1296 IM_RGB_TO_HSV(&c);
1297 IM_RGB_TO_HSV(inp);
1298 inp->channel[0] = c.channel[0];
1299 inp->channel[1] = c.channel[1];
1300 IM_HSV_TO_RGB(inp);
1301 inp->channel[3] = c.channel[3];
1302 ++outp;
1303 ++inp;
1304 }
1305
1306 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1307 }
1308}
1309
1310#undef IM_RGB_TO_HSV
1311#undef IM_HSV_TO_RGB
1312
9c106321 1313#/code