commit changes from draw branch
[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
TC
192#if 0
193 /* make sure our line buffer is big enough */
194 if (width > r->width) {
195 int new_width = r->width * 2;
196 if (new_width < width)
197 new_width = width;
198
199 if (r->im->bits <= 8) {
200 if (r->line_8)
201 r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
202 else
203 r->line_8 = mymalloc(sizeof(i_color) * new_width);
204 if (r->line_double) {
205 myfree(r->line_double);
206 r->line_double = NULL;
207 }
208 }
209 else {
210 if (r->line_double)
211 r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
212 else
213 r->line_double = mymalloc(sizeof(i_fcolor) * new_width);
214 if (r->line_8) {
215 myfree(r->line_8);
216 r->line_double = NULL;
217 }
218 }
219
220 r->width = new_width;
221 }
222#else
223 alloc_line(r, width, r->im->bits <= 8);
224#endif
225
226#code r->im->bits <= 8
227 /*if (r->IM_SUFFIX(line) == NULL)
228 r->IM_SUFFIX(line) = mymalloc(sizeof(IM_COLOR) * r->width);*/
229 (IM_SUFFIX(render_color_tab)[im->channels])(r, x, y, width, src, color);
230#/code
231}
232
233void
234i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
235 i_fill_t *fill) {
236 i_img *im = r->im;
237 int fill_channels = im->channels;
238
239 if (fill_channels == 1 || fill_channels == 3)
240 ++fill_channels;
241
242 if (y < 0 || y >= im->ysize)
243 return;
244 if (x < 0) {
245 width += x;
246 src -= x;
247 x = 0;
248 }
249 if (x + width > im->xsize) {
250 width = im->xsize - x;
251 }
252 if (x >= im->xsize || x + width <= 0 || width <= 0)
253 return;
254
255 if (src) {
256 /* avoid as much work as we can */
257 while (width > 0 && *src == 0) {
258 --width;
259 ++src;
260 ++x;
261 }
262 while (width > 0 && src[width-1] == 0) {
263 --width;
264 }
265 }
266 if (!width)
267 return;
268
269#if 0
270 if (r->im->bits <= 8 && fill->f_fill_with_color) {
271 if (!r->line_8)
272 r->line_8 = mymalloc(sizeof(i_color) * r->width);
273 if (!r->fill_line_8)
274 r->fill_line_8 = mymalloc(sizeof(i_color) * r->width);
275 }
276 else {
277 if (!r->line_double)
278 r->line_double = mymalloc(sizeof(i_fcolor) * r->width);
279 if (!r->fill_line_double)
280 r->fill_line_double = mymalloc(sizeof(i_fcolor) * r->width);
281 }
282
9c106321
TC
283 /* make sure our line buffer is big enough */
284 if (width > r->width) {
285 int new_width = r->width * 2;
286 if (new_width < width)
287 new_width = width;
288
9b1ec2b8 289 if (r->im->bits <= 8 && fill->f_fill_with_color) {
9c106321 290 r->line_8 = myrealloc(r->line_8, sizeof(i_color) * new_width);
9b1ec2b8
TC
291 r->fill_line_8 = myrealloc(r->fill_line_8, sizeof(i_color) * new_width);
292 if (r->line_double) {
293 myfree(r->line_double);
294 r->line_double = NULL;
295 }
296 if (r->fill_line_double) {
297 myfree(r->fill_line_double);
298 r->fill_line_double = NULL;
299 }
300 }
301 else {
9c106321 302 r->line_double = myrealloc(r->line_double, sizeof(i_fcolor) * new_width);
9b1ec2b8
TC
303 r->fill_line_double = myrealloc(r->fill_line_double, sizeof(i_fcolor) * new_width);
304 if (r->line_8) {
305 myfree(r->line_8);
306 r->line_8 = NULL;
307 }
308 if (r->fill_line_8) {
309 myfree(r->fill_line_8);
310 r->fill_line_8 = NULL;
311 }
312 }
313 r->width = new_width;
9c106321 314 }
9b1ec2b8
TC
315#else
316 alloc_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
317 alloc_fill_line(r, width, r->im->bits <= 8 && fill->f_fill_with_color != NULL);
318#endif
9c106321 319
9b1ec2b8
TC
320#code r->im->bits <= 8 && fill->f_fill_with_color
321 if (IM_FILL_COMBINE(fill)) {
322 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
323 IM_COLOR *destc = r->IM_SUFFIX(line);
324 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
325 if (src) {
326 unsigned char const *srcc = src;
327 IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
328 int work_width = width;
329 while (work_width) {
330 if (*srcc == 0) {
331 fillc->channel[fill_channels-1] = 0;
332 }
333 else if (*srcc != 255) {
334 fillc->channel[fill_channels-1] =
335 fillc->channel[fill_channels-1] * *srcc / 255;
336 }
337 --work_width;
338 ++srcc;
339 ++fillc;
340 }
341 }
342 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
343 IM_FILL_COMBINE(fill)(destc, srcc, r->im->channels, width);
344 }
345 else {
346 if (src) {
347 int work_width = width;
348 IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
349 IM_COLOR *destc = r->IM_SUFFIX(line);
350 int ch;
351
352 IM_FILL_FILLER(fill)(fill, x, y, width, fill_channels, r->IM_SUFFIX(fill_line));
353 IM_GLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
354 while (work_width) {
355 if (*src == 255) {
356 /* just replace it */
357 *destc = *srcc;
358 }
359 else if (*src) {
360 for (ch = 0; ch < im->channels; ++ch) {
361 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
362 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
363 destc->channel[ch] = IM_LIMIT(work);
364 }
365 }
366
367 ++srcc;
368 ++destc;
369 ++src;
370 --work_width;
371 }
372 }
373 else { /* if (src) */
374 IM_FILL_FILLER(fill)(fill, x, y, width, r->im->channels, r->IM_SUFFIX(line));
375 }
376 }
377 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
9c106321
TC
378#/code
379}
380
381static void
382dump_src(const char *note, unsigned char const *src, int width) {
383 int i;
384 printf("%s - %p/%d\n", note, src, width);
385 for (i = 0; i < width; ++i) {
386 printf("%02x ", src[i]);
387 }
388 putchar('\n');
389}
390
391#code
392
9b1ec2b8
TC
393void
394IM_RENDER_LINE(i_render *r, int x, int y, int width, const IM_SAMPLE_T *src,
395 IM_COLOR *line, IM_FILL_COMBINE_F combine) {
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) {
422 int work_width = width;
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) {
447 int work_width = width;
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
481IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width,
482 unsigned char const *src, i_color const *color) {
483 i_img *im = r->im;
484 IM_COLOR *linep = r->IM_SUFFIX(line);
485 int ch, channels = im->channels;
486 int fetch_offset;
487#undef STORE_COLOR
488#ifdef IM_EIGHT_BIT
489#define STORE_COLOR (*color)
490#else
491 i_fcolor fcolor;
492
493 for (ch = 0; ch < channels; ++ch) {
494 fcolor.channel[ch] = color->channel[ch] / 255.0;
495 }
496#define STORE_COLOR fcolor
497#endif
498
499 fetch_offset = 0;
500 while (fetch_offset < width && *src == 0xFF) {
501 *linep++ = STORE_COLOR;
502 ++src;
503 ++fetch_offset;
504 }
505 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
506 while (fetch_offset < width) {
507#ifdef IM_EIGHT_BIT
508 IM_WORK_T alpha = *src++;
509#else
510 IM_WORK_T alpha = *src++ / 255.0;
511#endif
512 if (alpha == IM_SAMPLE_MAX)
513 *linep = STORE_COLOR;
514 else if (alpha) {
515 for (ch = 0; ch < channels; ++ch) {
516 linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
517 + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
518 }
519 }
520 ++linep;
521 ++fetch_offset;
522 }
523 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
524}
525
526static
527void
528IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width,
529 unsigned char const *src, i_color const *color) {
530 IM_COLOR *linep = r->IM_SUFFIX(line);
531 int ch;
532 int alpha_channel = r->im->channels - 1;
533 int fetch_offset;
534#undef STORE_COLOR
535#ifdef IM_EIGHT_BIT
536#define STORE_COLOR (*color)
537#else
538 i_fcolor fcolor;
539
540 for (ch = 0; ch < r->im->channels; ++ch) {
541 fcolor.channel[ch] = color->channel[ch] / 255.0;
542 }
543#define STORE_COLOR fcolor
544#endif
545
546 fetch_offset = 0;
547 while (fetch_offset < width && *src == 0xFF) {
548 *linep++ = STORE_COLOR;
549 ++src;
550 ++fetch_offset;
551 }
552 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
553 while (fetch_offset < width) {
554#ifdef IM_EIGHT_BIT
555 IM_WORK_T src_alpha = *src++;
556#else
557 IM_WORK_T src_alpha = *src++ / 255.0;
558#endif
559 if (src_alpha == IM_SAMPLE_MAX)
560 *linep = STORE_COLOR;
561 else if (src_alpha) {
e6e94ab0 562 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
9c106321
TC
563 IM_WORK_T orig_alpha = linep->channel[alpha_channel];
564 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
565 for (ch = 0; ch < alpha_channel; ++ch) {
566 linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
567 + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
568 ) / dest_alpha;
569 }
570 linep->channel[alpha_channel] = dest_alpha;
571 }
572 ++linep;
573 ++fetch_offset;
574 }
575 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
9b1ec2b8
TC
576#undef STORE_COLOR
577}
578
579/* combine a line of image data with an output line, both the input
580 and output lines include an alpha channel.
581
582 Both input and output lines have I<channels> of data, channels
583 should be either 2 or 4.
584*/
585
586static void
587IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
588 int channels, int count) {
589 int ch;
590 int alpha_channel = channels - 1;
591
592 while (count) {
593 IM_WORK_T src_alpha = in->channel[alpha_channel];
594
595 if (src_alpha == IM_SAMPLE_MAX)
596 *out = *in;
597 else if (src_alpha) {
598 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
599 IM_WORK_T orig_alpha = out->channel[alpha_channel];
600 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
601
602 for (ch = 0; ch < alpha_channel; ++ch) {
603 out->channel[ch] = ( src_alpha * in->channel[ch]
604 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
605 ) / dest_alpha;
606 }
607 out->channel[alpha_channel] = dest_alpha;
608 }
609
610 ++out;
611 ++in;
612 --count;
613 }
614}
615
616/* combine a line of image data with an output line. The input line
617 includes an alpha channel, the output line has no alpha channel.
618
619 The input line has I<channels>+1 of color data. The output line
620 has I<channels> of color data.
621*/
622
623static void
624IM_SUFFIX(combine_line_noalpha)
625 (IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
626 int ch;
627
628 while (count) {
629 IM_WORK_T src_alpha = in->channel[channels];
630
631 if (src_alpha == IM_SAMPLE_MAX)
632 *out = *in;
633 else if (src_alpha) {
634 IM_WORK_T remains;
635
636 remains = IM_SAMPLE_MAX - src_alpha;
637 for (ch = 0; ch < channels; ++ch) {
638 out->channel[ch] = ( in->channel[ch] * src_alpha
639 + out->channel[ch] * remains) / IM_SAMPLE_MAX;
640 }
641 }
642
643 ++out;
644 ++in;
645 --count;
646 }
647}
648
649/* combine a line of image data with an output line, both the input
650 and output lines include an alpha channel.
651
652 Both input and output lines have I<channels> of data, channels
653 should be either 2 or 4.
654
655 This variant does not modify the output alpha channel.
656*/
657
658static void
659IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
660 int channels, int count) {
661 int ch;
662 int alpha_channel = channels - 1;
663
664 while (count) {
665 IM_WORK_T src_alpha = in->channel[alpha_channel];
666
667 if (src_alpha == IM_SAMPLE_MAX)
668 *out = *in;
669 else if (src_alpha) {
670 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
671 IM_WORK_T orig_alpha = out->channel[alpha_channel];
672 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
673
674 for (ch = 0; ch < alpha_channel; ++ch) {
675 out->channel[ch] = ( src_alpha * in->channel[ch]
676 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
677 ) / dest_alpha;
678 }
679 }
680
681 ++out;
682 ++in;
683 --count;
684 }
685}
686
687static void
688IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
689 if (channels == 2 || channels == 4)
690 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
691 else
692 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
693}
694
695static void
696IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
697 if (channels == 2 || channels == 4)
698 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
699 else
700 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
701}
702
703static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, int);
704static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, int);
705static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, int);
706static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, int);
707static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, int);
708static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, int);
709static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, int);
710static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, int);
711static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, int);
712static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, int);
713static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, int);
714static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, int);
715
716static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
717{
718 NULL,
719 IM_SUFFIX(combine_alphablend),
720 IM_SUFFIX(combine_mult),
721 IM_SUFFIX(combine_dissolve),
722 IM_SUFFIX(combine_add),
723 IM_SUFFIX(combine_subtract),
724 IM_SUFFIX(combine_diff),
725 IM_SUFFIX(combine_lighten),
726 IM_SUFFIX(combine_darken),
727 IM_SUFFIX(combine_hue),
728 IM_SUFFIX(combine_sat),
729 IM_SUFFIX(combine_value),
730 IM_SUFFIX(combine_color)
731};
732
733#/code
734
735/*
736=item i_get_combine(combine, color_func, fcolor_func)
737
738=cut
739*/
740
741void i_get_combine(int combine, i_fill_combine_f *color_func,
742 i_fill_combinef_f *fcolor_func) {
743 if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
744 combine = 0;
745
746 *color_func = combines_8[combine];
747 *fcolor_func = combines_double[combine];
748}
749
750#code
751
752/*
753 Three good references for implementing combining modes:
754
755 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
756 referenced as [svg1.2]
757
758 http://gimp-savvy.com/BOOK/index.html?node55.html
759 ("The Blending Modes", if it changes)
760 referenced as [savvy]
761
762 http://www.pegtop.net/delphi/articles/blendmodes/
763 referenced as [pegtop]
764
765 Where differences exist, I follow the SVG practice, the gimp
766 practice, and lastly pegtop.
767*/
768
769
770static void
771IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
772 IM_SUFFIX(combine_line)(out, in, channels, count);
773}
774
775/*
776Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
777Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
778 = Sa + Da - Sa.Da
779
780When Da=1
781
782Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
783 */
784static void
785IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
786 int ch;
787 IM_COLOR *inp = in;
788 IM_COLOR *outp = out;
789 int work_count = count;
790 int color_channels = i_color_channels(channels);
791
792 if (i_has_alpha(channels)) {
793 while (work_count--) {
794 IM_WORK_T orig_alpha = outp->channel[color_channels];
795 IM_WORK_T src_alpha = inp->channel[color_channels];
796
797 if (src_alpha) {
798 IM_WORK_T dest_alpha = src_alpha + orig_alpha
799 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
800
801 for (ch = 0; ch < color_channels; ++ch) {
802 outp->channel[ch] =
803 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
804 * orig_alpha
805 + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
806 + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
807 / IM_SAMPLE_MAX / dest_alpha;
808 }
809 outp->channel[color_channels] = dest_alpha;
810 }
811 ++outp;
812 ++inp;
813 }
814 }
815 else {
816 while (work_count--) {
817 IM_WORK_T src_alpha = inp->channel[color_channels];
818 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
819
820 if (src_alpha) {
821 for (ch = 0; ch < color_channels; ++ch) {
822 outp->channel[ch] =
823 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
824 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
825 }
826 }
827 ++outp;
828 ++inp;
829 }
830 }
831}
832
833static void
834IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
835 int color_channels = i_color_channels(channels);
836 int ch;
837
838 if (i_has_alpha(channels)) {
839 while (count--) {
840 if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
841 for (ch = 0; ch < color_channels; ++ch) {
842 out->channel[ch] = in->channel[ch];
843 }
844 out->channel[color_channels] = IM_SAMPLE_MAX;
845 }
846 ++out;
847 ++in;
848 }
849 }
850 else {
851 while (count--) {
852 if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
853 for (ch = 0; ch < color_channels; ++ch) {
854 out->channel[ch] = in->channel[ch];
855 }
856 }
857 ++out;
858 ++in;
859 }
860 }
861}
862
863/*
864Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
865 = Sca + Dca
866Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
867 = Sa + Da
868*/
869
870static void
871IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
872 int ch;
873 int color_channels = i_color_channels(channels);
874 int work_count = count;
875 IM_COLOR *inp = in;
876 IM_COLOR *outp = out;
877
878 if (i_has_alpha(channels)) {
879 while (work_count--) {
880 IM_WORK_T src_alpha = inp->channel[color_channels];
881 if (src_alpha) {
882 IM_WORK_T orig_alpha = outp->channel[color_channels];
883 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
884 if (dest_alpha > IM_SAMPLE_MAX)
885 dest_alpha = IM_SAMPLE_MAX;
886 for (ch = 0; ch < color_channels; ++ch) {
887 IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
888 if (total > IM_SAMPLE_MAX)
889 total = IM_SAMPLE_MAX;
890 outp->channel[ch] = total;
891 }
892 outp->channel[color_channels] = dest_alpha;
893 }
894
895 ++outp;
896 ++inp;
897 }
898 }
899 else {
900 while (work_count--) {
901 IM_WORK_T src_alpha = inp->channel[color_channels];
902 if (src_alpha) {
903 for (ch = 0; ch < color_channels; ++ch) {
904 IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
905 if (total > IM_SAMPLE_MAX)
906 total = IM_SAMPLE_MAX;
907 outp->channel[ch] = total;
908 }
909 }
910
911 ++outp;
912 ++inp;
913 }
914 }
915}
916
917/*
918 [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
919 it as max(A-B, 0). [svg1.2] doesn't cover it.
920
921 [savvy] doesn't document how it works with an alpha channel. GIMP
922 actually seems to calculate the final value then use the alpha
923 channel to apply that to the target.
924 */
925static void
926IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
927 int ch;
928 IM_COLOR const *inp = in;
929 IM_COLOR *outp = out;
930 int work_count = count;
931 int color_channels = i_color_channels(channels);
932
933 if (i_has_alpha(channels)) {
934 while (work_count--) {
935 IM_WORK_T src_alpha = inp->channel[color_channels];
936 if (src_alpha) {
937 IM_WORK_T orig_alpha = outp->channel[color_channels];
938 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
939 if (dest_alpha > IM_SAMPLE_MAX)
940 dest_alpha = IM_SAMPLE_MAX;
941 for (ch = 0; ch < color_channels; ++ch) {
942 IM_WORK_T total =
943 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
944 / dest_alpha;
945 if (total < 0)
946 total = 0;
947 outp->channel[ch] = total;
948 }
949 outp->channel[color_channels] = dest_alpha;
950 }
951 ++outp;
952 ++inp;
953 }
954 }
955 else {
956 while (work_count--) {
957 IM_WORK_T src_alpha = inp->channel[color_channels];
958 if (src_alpha) {
959 for (ch = 0; ch < color_channels; ++ch) {
960 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
961 if (total < 0)
962 total = 0;
963 outp->channel[ch] = total;
964 }
965 }
966 ++outp;
967 ++inp;
968 }
969 }
970}
971
972#ifdef IM_EIGHT_BIT
973#define IM_abs(x) abs(x)
974#else
975#define IM_abs(x) fabs(x)
976#endif
977
978/*
979Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
980 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
981Da' = Sa + Da - Sa.Da
982*/
983static void
984IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
985 int ch;
986 IM_COLOR const *inp = in;
987 IM_COLOR *outp = out;
988 int work_count = count;
989 int color_channels = i_color_channels(channels);
990
991 if (i_has_alpha(channels)) {
992 while (work_count--) {
993 IM_WORK_T src_alpha = inp->channel[color_channels];
994 if (src_alpha) {
995 IM_WORK_T orig_alpha = outp->channel[color_channels];
996 IM_WORK_T dest_alpha = src_alpha + orig_alpha
997 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
998 for (ch = 0; ch < color_channels; ++ch) {
999 IM_WORK_T src = inp->channel[ch] * src_alpha;
1000 IM_WORK_T orig = outp->channel[ch] * orig_alpha;
1001 IM_WORK_T src_da = src * orig_alpha;
1002 IM_WORK_T dest_sa = orig * src_alpha;
1003 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1004 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1005 }
1006 outp->channel[color_channels] = dest_alpha;
1007 }
1008 ++inp;
1009 ++outp;
1010 }
1011 }
1012 else {
1013 while (work_count--) {
1014 IM_WORK_T src_alpha = inp->channel[color_channels];
1015 if (src_alpha) {
1016 for (ch = 0; ch < color_channels; ++ch) {
1017 IM_WORK_T src = inp->channel[ch] * src_alpha;
1018 IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1019 IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1020 IM_WORK_T dest_sa = orig * src_alpha;
1021 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1022 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1023 }
1024 }
1025 ++inp;
1026 ++outp;
1027 }
1028 }
1029}
1030
1031#undef IM_abs
1032
1033/*
1034 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1035 Da' = Sa + Da - Sa.Da
1036
1037 To hoist some code:
1038
1039 Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1040 = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1041
1042 When Da=1:
1043
1044 Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1045 = min(Sca, Dc.Sa) + Dc(1-Sa)
1046 = Sa.min(Sc, Dc) + Dc - Dc.Sa
1047 Da' = Sa + 1 - Sa.1
1048 = 1
1049 */
1050static void
1051IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
1052 int count) {
1053 int ch;
1054 IM_COLOR const *inp = in;
1055 IM_COLOR *outp = out;
1056 int work_count = count;
1057 int color_channels = i_color_channels(channels);
1058
1059 if (i_has_alpha(channels)) {
1060 while (work_count--) {
1061 IM_WORK_T src_alpha = inp->channel[color_channels];
1062
1063 if (src_alpha) {
1064 IM_WORK_T orig_alpha = outp->channel[color_channels];
1065 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1066 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1067 for (ch = 0; ch < color_channels; ++ch) {
1068 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1069 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1070 IM_WORK_T ScaDa = Sca * orig_alpha;
1071 IM_WORK_T DcaSa = Dca * src_alpha;
1072 IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1073 outp->channel[ch] =
1074 (
1075 minc + (Sca + Dca) * IM_SAMPLE_MAX
1076 - ScaDa - DcaSa
1077 ) / (IM_SAMPLE_MAX * dest_alpha);
1078 }
1079 outp->channel[color_channels] = dest_alpha;
1080 }
1081 ++outp;
1082 ++inp;
1083 }
1084 }
1085 else {
1086 while (work_count--) {
1087 IM_WORK_T src_alpha = inp->channel[color_channels];
1088
1089 if (src_alpha) {
1090 for (ch = 0; ch < color_channels; ++ch) {
1091 IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1092 ? outp->channel[ch] : inp->channel[ch];
1093 outp->channel[ch] =
1094 (
1095 src_alpha * minc +
1096 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1097 ) / IM_SAMPLE_MAX;
1098 }
1099 }
1100 ++outp;
1101 ++inp;
1102 }
1103 }
1104}
1105
1106static void
1107IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1108 int ch;
1109 IM_COLOR const *inp = in;
1110 IM_COLOR *outp = out;
1111 int work_count = count;
1112 int color_channels = i_color_channels(channels);
1113
1114 if (i_has_alpha(channels)) {
1115 while (work_count--) {
1116 IM_WORK_T src_alpha = inp->channel[color_channels];
1117
1118 if (src_alpha) {
1119 IM_WORK_T orig_alpha = outp->channel[color_channels];
1120 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1121 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1122 for (ch = 0; ch < color_channels; ++ch) {
1123 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1124 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1125 IM_WORK_T ScaDa = Sca * orig_alpha;
1126 IM_WORK_T DcaSa = Dca * src_alpha;
1127 IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1128 outp->channel[ch] =
1129 (
1130 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1131 - ScaDa - DcaSa
1132 ) / (IM_SAMPLE_MAX * dest_alpha);
1133 }
1134 outp->channel[color_channels] = dest_alpha;
1135 }
1136 ++outp;
1137 ++inp;
1138 }
1139 }
1140 else {
1141 while (work_count--) {
1142 IM_WORK_T src_alpha = inp->channel[color_channels];
1143
1144 if (src_alpha) {
1145 for (ch = 0; ch < color_channels; ++ch) {
1146 IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1147 ? outp->channel[ch] : inp->channel[ch];
1148 outp->channel[ch] =
1149 (
1150 src_alpha * maxc +
1151 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1152 ) / IM_SAMPLE_MAX;
1153 }
1154 }
1155 ++outp;
1156 ++inp;
1157 }
1158 }
1159}
1160
1161#if IM_EIGHT_BIT
1162#define IM_RGB_TO_HSV i_rgb_to_hsv
1163#define IM_HSV_TO_RGB i_hsv_to_rgb
1164#else
1165#define IM_RGB_TO_HSV i_rgb_to_hsvf
1166#define IM_HSV_TO_RGB i_hsv_to_rgbf
1167#endif
1168
1169static void
1170IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1171 if (channels > 2) {
1172 IM_COLOR *inp = in;
1173 IM_COLOR const *outp = out;
1174 int work_count = count;
1175
1176 if (i_has_alpha(channels)) {
1177 while (work_count--) {
1178 IM_COLOR c = *inp;
1179 IM_RGB_TO_HSV(&c);
1180 /* only transfer hue if there's saturation */
1181 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1182 *inp = *outp;
1183 IM_RGB_TO_HSV(inp);
1184 /* and no point in setting the target hue if the target has no sat */
1185 if (inp->channel[1]) {
1186 inp->channel[0] = c.channel[0];
1187 IM_HSV_TO_RGB(inp);
1188 inp->channel[3] = c.channel[3];
1189 }
1190 else {
1191 inp->channel[3] = 0;
1192 }
1193 }
1194 else {
1195 inp->channel[3] = 0;
1196 }
1197
1198 ++outp;
1199 ++inp;
1200 }
1201 }
1202 else {
1203 while (work_count--) {
1204 IM_COLOR c = *inp;
1205 IM_RGB_TO_HSV(&c);
1206 /* only transfer hue if there's saturation */
1207 if (c.channel[1] && inp->channel[3]) {
1208 *inp = *outp;
1209 IM_RGB_TO_HSV(inp);
1210 /* and no point in setting the target hue if the target has no sat */
1211 if (inp->channel[1]) {
1212 inp->channel[0] = c.channel[0];
1213 IM_HSV_TO_RGB(inp);
1214 inp->channel[3] = c.channel[3];
1215 }
1216 }
1217 else {
1218 inp->channel[3] = 0;
1219 }
1220
1221 ++outp;
1222 ++inp;
1223 }
1224 }
1225
1226 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1227 }
9c106321
TC
1228}
1229
9b1ec2b8
TC
1230static void
1231IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1232 if (channels > 2) {
1233 IM_COLOR *inp = in;
1234 IM_COLOR const *outp = out;
1235 int work_count = count;
1236
1237 while (work_count--) {
1238 IM_COLOR c = *inp;
1239 *inp = *outp;
1240 IM_RGB_TO_HSV(&c);
1241 IM_RGB_TO_HSV(inp);
1242 inp->channel[1] = c.channel[1];
1243 IM_HSV_TO_RGB(inp);
1244 inp->channel[3] = c.channel[3];
1245 ++outp;
1246 ++inp;
1247 }
1248
1249 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1250 }
1251}
1252
1253static void
1254IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1255 if (channels > 2) {
1256 IM_COLOR *inp = in;
1257 IM_COLOR const *outp = out;
1258 int work_count = count;
1259
1260 while (work_count--) {
1261 IM_COLOR c = *inp;
1262 *inp = *outp;
1263 IM_RGB_TO_HSV(&c);
1264 IM_RGB_TO_HSV(inp);
1265 inp->channel[2] = c.channel[2];
1266 IM_HSV_TO_RGB(inp);
1267 inp->channel[3] = c.channel[3];
1268 ++outp;
1269 ++inp;
1270 }
1271 }
1272
1273 /* all images have a "value channel" - for greyscale it's the only
1274 colour channel */
1275 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1276}
1277
1278static void
1279IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
1280 if (channels > 2) {
1281 IM_COLOR *inp = in;
1282 IM_COLOR const *outp = out;
1283 int work_count = count;
1284
1285 while (work_count--) {
1286 IM_COLOR c = *inp;
1287 *inp = *outp;
1288 IM_RGB_TO_HSV(&c);
1289 IM_RGB_TO_HSV(inp);
1290 inp->channel[0] = c.channel[0];
1291 inp->channel[1] = c.channel[1];
1292 IM_HSV_TO_RGB(inp);
1293 inp->channel[3] = c.channel[3];
1294 ++outp;
1295 ++inp;
1296 }
1297
1298 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1299 }
1300}
1301
1302#undef IM_RGB_TO_HSV
1303#undef IM_HSV_TO_RGB
1304
9c106321 1305#/code