flood_fill() wouldn't fill the right side of a single scan-line fill area.
[imager.git] / render.im
CommitLineData
9c106321
TC
1/*
2Render utilities
3*/
4#include "imager.h"
5
6#define RENDER_MAGIC 0x765AE
7
8typedef void (*render_color_f)(i_render *, int, int, int, unsigned char const *src, i_color const *color);
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
16static void IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
17static void IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
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
9b1ec2b8
TC
28static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
29static 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 */
31static void IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
32
33static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
34static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
35
9c106321
TC
36#/code
37
38void
39i_render_init(i_render *r, i_img *im, int width) {
40 r->magic = RENDER_MAGIC;
41 r->im = im;
9b1ec2b8 42 r->line_width = width;
9c106321
TC
43 r->line_8 = NULL;
44 r->line_double = NULL;
9b1ec2b8
TC
45 r->fill_width = width;
46 r->fill_line_8 = NULL;
47 r->fill_line_double = NULL;
9c106321
TC
48}
49
50void
51i_render_done(i_render *r) {
52 if (r->line_8)
53 myfree(r->line_8);
9b1ec2b8 54 if (r->line_double)
9c106321 55 myfree(r->line_double);
9b1ec2b8
TC
56 if (r->fill_line_8)
57 myfree(r->fill_line_8);
58 if (r->fill_line_double)
59 myfree(r->fill_line_double);
9c106321
TC
60 r->magic = 0;
61}
62
9b1ec2b8
TC
63static void
64alloc_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)
68 new_width = width;
69
70 if (eight_bit) {
71 if (r->line_8)
72 r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
73 else
74 r->line_8 = mymalloc(sizeof(i_color) * new_width);
75 if (r->line_double) {
76 myfree(r->line_double);
77 r->line_double = NULL;
78 }
79 }
80 else {
81 if (r->line_double)
82 r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
83 else
84 r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
85 if (r->line_8) {
86 myfree(r->line_8);
87 r->line_double = NULL;
88 }
89 }
90
91 r->line_width = new_width;
92 }
93 else {
94 if (eight_bit) {
95 if (!r->line_8)
96 r->line_8 = mymalloc(sizeof(i_color) * r->line_width);
97 if (r->line_double) {
98 myfree(r->line_double);
99 r->line_double = NULL;
100 }
101 }
102 else {
103 if (!r->line_double)
104 r->line_double = mymalloc(sizeof(i_fcolor) * r->line_width);
105 if (r->line_8) {
106 myfree(r->line_8);
107 r->line_8 = NULL;
108 }
109 }
110 }
111}
112
113static void
114alloc_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)
118 new_width = width;
119
120 if (eight_bit) {
121 if (r->line_8)
122 r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
123 else
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;
128 }
129 }
130 else {
131 if (r->fill_line_double)
132 r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
133 else
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;
138 }
139 }
140
141 r->fill_width = new_width;
142 }
143 else {
144 if (eight_bit) {
145 if (!r->fill_line_8)
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;
150 }
151 }
152 else {
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;
158 }
159 }
160 }
161}
162
9c106321
TC
163void
164i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
165 i_color const *color) {
166 i_img *im = r->im;
167 if (y < 0 || y >= im->ysize)
168 return;
169 if (x < 0) {
170 width += x;
171 src -= x;
172 x = 0;
173 }
174 if (x + width > im->xsize) {
175 width = im->xsize - x;
176 }
177 if (x >= im->xsize || x + width <= 0 || width <= 0)
178 return;
179
180 /* avoid as much work as we can */
181 while (width > 0 && *src == 0) {
182 --width;
183 ++src;
184 ++x;
185 }
186 while (width > 0 && src[width-1] == 0) {
187 --width;
188 }
189 if (!width)
190 return;
191
9b1ec2b8 192 alloc_line(r, width, r->im->bits <= 8);
9b1ec2b8
TC
193
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);
198#/code
199}
200
201void
202i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
203 i_fill_t *fill) {
204 i_img *im = r->im;
205 int fill_channels = im->channels;
206
207 if (fill_channels == 1 || fill_channels == 3)
208 ++fill_channels;
209
210 if (y < 0 || y >= im->ysize)
211 return;
212 if (x < 0) {
213 width += x;
214 src -= x;
215 x = 0;
216 }
217 if (x + width > im->xsize) {
218 width = im->xsize - x;
219 }
220 if (x >= im->xsize || x + width <= 0 || width <= 0)
221 return;
222
223 if (src) {
224 /* avoid as much work as we can */
225 while (width > 0 && *src == 0) {
226 --width;
227 ++src;
228 ++x;
229 }
230 while (width > 0 && src[width-1] == 0) {
231 --width;
232 }
233 }
234 if (!width)
235 return;
236
9b1ec2b8
TC
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);
9c106321 239
9b1ec2b8
TC
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));
245 if (src) {
246 unsigned char const *srcc = src;
247 IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
248 int work_width = width;
249 while (work_width) {
250 if (*srcc == 0) {
251 fillc->channel[fill_channels-1] = 0;
252 }
253 else if (*srcc != 255) {
254 fillc->channel[fill_channels-1] =
255 fillc->channel[fill_channels-1] * *srcc / 255;
256 }
257 --work_width;
258 ++srcc;
259 ++fillc;
260 }
261 }
262 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
263 IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
264 }
265 else {
266 if (src) {
267 int work_width = width;
268 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
269 IM_COLOR *destc = r->IM_SUFFIX(line);
270 int ch;
271
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));
274 while (work_width) {
275 if (*src == 255) {
276 /* just replace it */
277 *destc = *srcc;
278 }
279 else if (*src) {
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);
284 }
285 }
286
287 ++srcc;
288 ++destc;
289 ++src;
290 --work_width;
291 }
292 }
293 else { /* if (src) */
e958b64e 294 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(line));
9b1ec2b8
TC
295 }
296 }
297 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
9c106321
TC
298#/code
299}
300
301static void
302dump_src(const char *note, unsigned char const *src, int width) {
303 int i;
304 printf("%s - %p/%d\n", note, src, width);
305 for (i = 0; i < width; ++i) {
306 printf("%02x ", src[i]);
307 }
308 putchar('\n');
309}
310
311#code
312
9b1ec2b8
TC
313void
314IM_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) {
316 i_img *im = r->im;
317 int src_chans = im->channels;
318
319 /* src must always have an alpha channel */
320 if (src_chans == 1 || src_chans == 3)
321 ++src_chans;
322
323 if (y < 0 || y >= im->ysize)
324 return;
325 if (x < 0) {
326 src -= x;
327 line -= x;
328 width += x;
329 x = 0;
330 }
331 if (x + width > im->xsize)
332 width = r->im->xsize - x;
333
334#ifdef IM_EIGHT_BIT
335 alloc_line(r, width, 1);
336#else
337 alloc_line(r, width, 0);
338#endif
339
340 if (combine) {
341 if (src) {
342 int work_width = width;
343 IM_COLOR *linep = line;
344 const IM_SAMPLE_T *srcp = src;
345 int alpha_chan = src_chans - 1;
346
347 while (work_width) {
348 if (*srcp) {
349 if (*srcp != IM_SAMPLE_MAX)
350 linep->channel[alpha_chan] =
351 linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
352 }
353 else {
354 linep->channel[alpha_chan] = 0;
355 }
356 --work_width;
357 ++srcp;
358 ++linep;
359 }
360 }
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));
364 }
365 else {
366 if (src) {
367 int work_width = width;
368 IM_COLOR *srcc = line;
369 IM_COLOR *destc = r->IM_SUFFIX(line);
370
371 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
372 while (work_width) {
373 if (*src == 255) {
374 /* just replace it */
375 *destc = *srcc;
376 }
377 else if (*src) {
378 int ch;
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);
383 }
384 }
385
386 ++srcc;
387 ++destc;
388 ++src;
389 --work_width;
390 }
391 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
392 }
393 else {
394 IM_PLIN(im, x, x+width, y, line);
395 }
396 }
397}
398
9c106321
TC
399static
400void
401IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width,
402 unsigned char const *src, i_color const *color) {
403 i_img *im = r->im;
404 IM_COLOR *linep = r->IM_SUFFIX(line);
405 int ch, channels = im->channels;
406 int fetch_offset;
407#undef STORE_COLOR
408#ifdef IM_EIGHT_BIT
409#define STORE_COLOR (*color)
410#else
411 i_fcolor fcolor;
412
413 for (ch = 0; ch < channels; ++ch) {
414 fcolor.channel[ch] = color->channel[ch] / 255.0;
415 }
416#define STORE_COLOR fcolor
417#endif
418
419 fetch_offset = 0;
420 while (fetch_offset < width && *src == 0xFF) {
421 *linep++ = STORE_COLOR;
422 ++src;
423 ++fetch_offset;
424 }
425 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
426 while (fetch_offset < width) {
427#ifdef IM_EIGHT_BIT
428 IM_WORK_T alpha = *src++;
429#else
430 IM_WORK_T alpha = *src++ / 255.0;
431#endif
432 if (alpha == IM_SAMPLE_MAX)
433 *linep = STORE_COLOR;
434 else if (alpha) {
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;
438 }
439 }
440 ++linep;
441 ++fetch_offset;
442 }
443 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
444}
445
446static
447void
448IM_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);
451 int ch;
452 int alpha_channel = r->im->channels - 1;
453 int fetch_offset;
454#undef STORE_COLOR
455#ifdef IM_EIGHT_BIT
456#define STORE_COLOR (*color)
457#else
458 i_fcolor fcolor;
459
460 for (ch = 0; ch < r->im->channels; ++ch) {
461 fcolor.channel[ch] = color->channel[ch] / 255.0;
462 }
463#define STORE_COLOR fcolor
464#endif
465
466 fetch_offset = 0;
467 while (fetch_offset < width && *src == 0xFF) {
468 *linep++ = STORE_COLOR;
469 ++src;
470 ++fetch_offset;
471 }
472 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
473 while (fetch_offset < width) {
474#ifdef IM_EIGHT_BIT
475 IM_WORK_T src_alpha = *src++;
476#else
477 IM_WORK_T src_alpha = *src++ / 255.0;
478#endif
479 if (src_alpha == IM_SAMPLE_MAX)
480 *linep = STORE_COLOR;
481 else if (src_alpha) {
e6e94ab0 482 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
9c106321
TC
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
488 ) / dest_alpha;
489 }
490 linep->channel[alpha_channel] = dest_alpha;
491 }
492 ++linep;
493 ++fetch_offset;
494 }
495 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
9b1ec2b8
TC
496#undef STORE_COLOR
497}
498
499/* combine a line of image data with an output line, both the input
500 and output lines include an alpha channel.
501
502 Both input and output lines have I<channels> of data, channels
503 should be either 2 or 4.
504*/
505
506static void
507IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
508 int channels, int count) {
509 int ch;
510 int alpha_channel = channels - 1;
511
512 while (count) {
513 IM_WORK_T src_alpha = in->channel[alpha_channel];
514
515 if (src_alpha == IM_SAMPLE_MAX)
516 *out = *in;
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;
521
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
525 ) / dest_alpha;
526 }
527 out->channel[alpha_channel] = dest_alpha;
528 }
529
530 ++out;
531 ++in;
532 --count;
533 }
534}
535
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.
538
539 The input line has I<channels>+1 of color data. The output line
540 has I<channels> of color data.
541*/
542
543static void
544IM_SUFFIX(combine_line_noalpha)
545 (IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
546 int ch;
547
548 while (count) {
549 IM_WORK_T src_alpha = in->channel[channels];
550
551 if (src_alpha == IM_SAMPLE_MAX)
552 *out = *in;
553 else if (src_alpha) {
554 IM_WORK_T remains;
555
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;
560 }
561 }
562
563 ++out;
564 ++in;
565 --count;
566 }
567}
568
569/* combine a line of image data with an output line, both the input
570 and output lines include an alpha channel.
571
572 Both input and output lines have I<channels> of data, channels
573 should be either 2 or 4.
574
575 This variant does not modify the output alpha channel.
576*/
577
578static void
579IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
580 int channels, int count) {
581 int ch;
582 int alpha_channel = channels - 1;
583
584 while (count) {
585 IM_WORK_T src_alpha = in->channel[alpha_channel];
586
587 if (src_alpha == IM_SAMPLE_MAX)
588 *out = *in;
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;
593
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
597 ) / dest_alpha;
598 }
599 }
600
601 ++out;
602 ++in;
603 --count;
604 }
605}
606
607static void
608IM_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);
611 else
612 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
613}
614
615static void
616IM_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);
619 else
620 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
621}
622
623static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, int);
624static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, int);
625static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, int);
626static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, int);
627static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, int);
628static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, int);
629static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, int);
630static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, int);
631static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, int);
632static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, int);
633static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, int);
634static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, int);
635
636static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
637{
638 NULL,
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)
651};
652
653#/code
654
655/*
656=item i_get_combine(combine, color_func, fcolor_func)
657
658=cut
659*/
660
661void 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))
664 combine = 0;
665
666 *color_func = combines_8[combine];
667 *fcolor_func = combines_double[combine];
668}
669
670#code
671
672/*
673 Three good references for implementing combining modes:
674
675 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
676 referenced as [svg1.2]
677
678 http://gimp-savvy.com/BOOK/index.html?node55.html
679 ("The Blending Modes", if it changes)
680 referenced as [savvy]
681
682 http://www.pegtop.net/delphi/articles/blendmodes/
683 referenced as [pegtop]
684
685 Where differences exist, I follow the SVG practice, the gimp
686 practice, and lastly pegtop.
687*/
688
689
690static void
691IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
692 IM_SUFFIX(combine_line)(out, in, channels, count);
693}
694
695/*
696Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
697Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
698 = Sa + Da - Sa.Da
699
700When Da=1
701
702Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
703 */
704static void
705IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
706 int ch;
707 IM_COLOR *inp = in;
708 IM_COLOR *outp = out;
709 int work_count = count;
710 int color_channels = i_color_channels(channels);
711
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];
716
717 if (src_alpha) {
718 IM_WORK_T dest_alpha = src_alpha + orig_alpha
719 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
720
721 for (ch = 0; ch < color_channels; ++ch) {
722 outp->channel[ch] =
723 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
724 * orig_alpha
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;
728 }
729 outp->channel[color_channels] = dest_alpha;
730 }
731 ++outp;
732 ++inp;
733 }
734 }
735 else {
736 while (work_count--) {
737 IM_WORK_T src_alpha = inp->channel[color_channels];
738 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
739
740 if (src_alpha) {
741 for (ch = 0; ch < color_channels; ++ch) {
742 outp->channel[ch] =
743 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
744 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
745 }
746 }
747 ++outp;
748 ++inp;
749 }
750 }
751}
752
753static void
754IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
755 int color_channels = i_color_channels(channels);
756 int ch;
757
758 if (i_has_alpha(channels)) {
759 while (count--) {
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];
763 }
764 out->channel[color_channels] = IM_SAMPLE_MAX;
765 }
766 ++out;
767 ++in;
768 }
769 }
770 else {
771 while (count--) {
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];
775 }
776 }
777 ++out;
778 ++in;
779 }
780 }
781}
782
783/*
784Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
785 = Sca + Dca
786Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
787 = Sa + Da
788*/
789
790static void
791IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
792 int ch;
793 int color_channels = i_color_channels(channels);
794 int work_count = count;
795 IM_COLOR *inp = in;
796 IM_COLOR *outp = out;
797
798 if (i_has_alpha(channels)) {
799 while (work_count--) {
800 IM_WORK_T src_alpha = inp->channel[color_channels];
801 if (src_alpha) {
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;
811 }
812 outp->channel[color_channels] = dest_alpha;
813 }
814
815 ++outp;
816 ++inp;
817 }
818 }
819 else {
820 while (work_count--) {
821 IM_WORK_T src_alpha = inp->channel[color_channels];
822 if (src_alpha) {
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;
828 }
829 }
830
831 ++outp;
832 ++inp;
833 }
834 }
835}
836
837/*
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.
840
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.
844 */
845static void
846IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
847 int ch;
848 IM_COLOR const *inp = in;
849 IM_COLOR *outp = out;
850 int work_count = count;
851 int color_channels = i_color_channels(channels);
852
853 if (i_has_alpha(channels)) {
854 while (work_count--) {
855 IM_WORK_T src_alpha = inp->channel[color_channels];
856 if (src_alpha) {
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) {
862 IM_WORK_T total =
863 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
864 / dest_alpha;
865 if (total < 0)
866 total = 0;
867 outp->channel[ch] = total;
868 }
869 outp->channel[color_channels] = dest_alpha;
870 }
871 ++outp;
872 ++inp;
873 }
874 }
875 else {
876 while (work_count--) {
877 IM_WORK_T src_alpha = inp->channel[color_channels];
878 if (src_alpha) {
879 for (ch = 0; ch < color_channels; ++ch) {
880 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
881 if (total < 0)
882 total = 0;
883 outp->channel[ch] = total;
884 }
885 }
886 ++outp;
887 ++inp;
888 }
889 }
890}
891
892#ifdef IM_EIGHT_BIT
893#define IM_abs(x) abs(x)
894#else
895#define IM_abs(x) fabs(x)
896#endif
897
898/*
899Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
900 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
901Da' = Sa + Da - Sa.Da
902*/
903static void
904IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
905 int ch;
906 IM_COLOR const *inp = in;
907 IM_COLOR *outp = out;
908 int work_count = count;
909 int color_channels = i_color_channels(channels);
910
911 if (i_has_alpha(channels)) {
912 while (work_count--) {
913 IM_WORK_T src_alpha = inp->channel[color_channels];
914 if (src_alpha) {
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;
925 }
926 outp->channel[color_channels] = dest_alpha;
927 }
928 ++inp;
929 ++outp;
930 }
931 }
932 else {
933 while (work_count--) {
934 IM_WORK_T src_alpha = inp->channel[color_channels];
935 if (src_alpha) {
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;
943 }
944 }
945 ++inp;
946 ++outp;
947 }
948 }
949}
950
951#undef IM_abs
952
953/*
954 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
955 Da' = Sa + Da - Sa.Da
956
957 To hoist some code:
958
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
961
962 When Da=1:
963
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
967 Da' = Sa + 1 - Sa.1
968 = 1
969 */
970static void
971IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
972 int count) {
973 int ch;
974 IM_COLOR const *inp = in;
975 IM_COLOR *outp = out;
976 int work_count = count;
977 int color_channels = i_color_channels(channels);
978
979 if (i_has_alpha(channels)) {
980 while (work_count--) {
981 IM_WORK_T src_alpha = inp->channel[color_channels];
982
983 if (src_alpha) {
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;
993 outp->channel[ch] =
994 (
995 minc + (Sca + Dca) * IM_SAMPLE_MAX
996 - ScaDa - DcaSa
997 ) / (IM_SAMPLE_MAX * dest_alpha);
998 }
999 outp->channel[color_channels] = dest_alpha;
1000 }
1001 ++outp;
1002 ++inp;
1003 }
1004 }
1005 else {
1006 while (work_count--) {
1007 IM_WORK_T src_alpha = inp->channel[color_channels];
1008
1009 if (src_alpha) {
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];
1013 outp->channel[ch] =
1014 (
1015 src_alpha * minc +
1016 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1017 ) / IM_SAMPLE_MAX;
1018 }
1019 }
1020 ++outp;
1021 ++inp;
1022 }
1023 }
1024}
1025
1026static void
1027IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1028 int ch;
1029 IM_COLOR const *inp = in;
1030 IM_COLOR *outp = out;
1031 int work_count = count;
1032 int color_channels = i_color_channels(channels);
1033
1034 if (i_has_alpha(channels)) {
1035 while (work_count--) {
1036 IM_WORK_T src_alpha = inp->channel[color_channels];
1037
1038 if (src_alpha) {
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;
1048 outp->channel[ch] =
1049 (
1050 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1051 - ScaDa - DcaSa
1052 ) / (IM_SAMPLE_MAX * dest_alpha);
1053 }
1054 outp->channel[color_channels] = dest_alpha;
1055 }
1056 ++outp;
1057 ++inp;
1058 }
1059 }
1060 else {
1061 while (work_count--) {
1062 IM_WORK_T src_alpha = inp->channel[color_channels];
1063
1064 if (src_alpha) {
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];
1068 outp->channel[ch] =
1069 (
1070 src_alpha * maxc +
1071 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1072 ) / IM_SAMPLE_MAX;
1073 }
1074 }
1075 ++outp;
1076 ++inp;
1077 }
1078 }
1079}
1080
1081#if IM_EIGHT_BIT
1082#define IM_RGB_TO_HSV i_rgb_to_hsv
1083#define IM_HSV_TO_RGB i_hsv_to_rgb
1084#else
1085#define IM_RGB_TO_HSV i_rgb_to_hsvf
1086#define IM_HSV_TO_RGB i_hsv_to_rgbf
1087#endif
1088
1089static void
1090IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1091 if (channels > 2) {
1092 IM_COLOR *inp = in;
1093 IM_COLOR const *outp = out;
1094 int work_count = count;
1095
1096 if (i_has_alpha(channels)) {
1097 while (work_count--) {
1098 IM_COLOR c = *inp;
1099 IM_RGB_TO_HSV(&c);
1100 /* only transfer hue if there's saturation */
1101 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1102 *inp = *outp;
1103 IM_RGB_TO_HSV(inp);
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];
1107 IM_HSV_TO_RGB(inp);
1108 inp->channel[3] = c.channel[3];
1109 }
1110 else {
1111 inp->channel[3] = 0;
1112 }
1113 }
1114 else {
1115 inp->channel[3] = 0;
1116 }
1117
1118 ++outp;
1119 ++inp;
1120 }
1121 }
1122 else {
1123 while (work_count--) {
1124 IM_COLOR c = *inp;
1125 IM_RGB_TO_HSV(&c);
1126 /* only transfer hue if there's saturation */
1127 if (c.channel[1] && inp->channel[3]) {
1128 *inp = *outp;
1129 IM_RGB_TO_HSV(inp);
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];
1133 IM_HSV_TO_RGB(inp);
1134 inp->channel[3] = c.channel[3];
1135 }
1136 }
1137 else {
1138 inp->channel[3] = 0;
1139 }
1140
1141 ++outp;
1142 ++inp;
1143 }
1144 }
1145
1146 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1147 }
9c106321
TC
1148}
1149
9b1ec2b8
TC
1150static void
1151IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1152 if (channels > 2) {
1153 IM_COLOR *inp = in;
1154 IM_COLOR const *outp = out;
1155 int work_count = count;
1156
1157 while (work_count--) {
1158 IM_COLOR c = *inp;
1159 *inp = *outp;
1160 IM_RGB_TO_HSV(&c);
1161 IM_RGB_TO_HSV(inp);
1162 inp->channel[1] = c.channel[1];
1163 IM_HSV_TO_RGB(inp);
1164 inp->channel[3] = c.channel[3];
1165 ++outp;
1166 ++inp;
1167 }
1168
1169 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1170 }
1171}
1172
1173static void
1174IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1175 if (channels > 2) {
1176 IM_COLOR *inp = in;
1177 IM_COLOR const *outp = out;
1178 int work_count = count;
1179
1180 while (work_count--) {
1181 IM_COLOR c = *inp;
1182 *inp = *outp;
1183 IM_RGB_TO_HSV(&c);
1184 IM_RGB_TO_HSV(inp);
1185 inp->channel[2] = c.channel[2];
1186 IM_HSV_TO_RGB(inp);
1187 inp->channel[3] = c.channel[3];
1188 ++outp;
1189 ++inp;
1190 }
1191 }
1192
1193 /* all images have a "value channel" - for greyscale it's the only
1194 colour channel */
1195 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1196}
1197
1198static void
1199IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1200 if (channels > 2) {
1201 IM_COLOR *inp = in;
1202 IM_COLOR const *outp = out;
1203 int work_count = count;
1204
1205 while (work_count--) {
1206 IM_COLOR c = *inp;
1207 *inp = *outp;
1208 IM_RGB_TO_HSV(&c);
1209 IM_RGB_TO_HSV(inp);
1210 inp->channel[0] = c.channel[0];
1211 inp->channel[1] = c.channel[1];
1212 IM_HSV_TO_RGB(inp);
1213 inp->channel[3] = c.channel[3];
1214 ++outp;
1215 ++inp;
1216 }
1217
1218 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1219 }
1220}
1221
1222#undef IM_RGB_TO_HSV
1223#undef IM_HSV_TO_RGB
1224
9c106321 1225#/code