0.84 release
[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) {
336 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
337 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
338 destc->channel[ch] = IM_LIMIT(work);
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
356static void
50c75381
TC
357dump_src(const char *note, unsigned char const *src, i_img_dim width) {
358 i_img_dim i;
9c106321
TC
359 printf("%s - %p/%d\n", note, src, width);
360 for (i = 0; i < width; ++i) {
361 printf("%02x ", src[i]);
362 }
363 putchar('\n');
364}
365
366#code
367
50c75381
TC
368/*
369=item i_render_line(r, x, y, width, source, fill)
370=category Blit tools
371
372Render the given fill with the coverage in C<source[0]> through
373C<source[width-1]>.
374
375=cut
376
377=item i_render_linef(r, x, y, width, source, fill)
378=category Blit tools
379
380Render the given fill with the coverage in C<source[0]> through
381C<source[width-1]>.
382
383=cut
384*/
385
9b1ec2b8 386void
50c75381
TC
387IM_RENDER_LINE(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
388 const IM_SAMPLE_T *src, IM_COLOR *line,
389 IM_FILL_COMBINE_F combine) {
9b1ec2b8
TC
390 i_img *im = r->im;
391 int src_chans = im->channels;
392
393 /* src must always have an alpha channel */
394 if (src_chans == 1 || src_chans == 3)
395 ++src_chans;
396
397 if (y < 0 || y >= im->ysize)
398 return;
399 if (x < 0) {
400 src -= x;
401 line -= x;
402 width += x;
403 x = 0;
404 }
405 if (x + width > im->xsize)
406 width = r->im->xsize - x;
407
408#ifdef IM_EIGHT_BIT
409 alloc_line(r, width, 1);
410#else
411 alloc_line(r, width, 0);
412#endif
413
414 if (combine) {
415 if (src) {
50c75381 416 i_img_dim work_width = width;
9b1ec2b8
TC
417 IM_COLOR *linep = line;
418 const IM_SAMPLE_T *srcp = src;
419 int alpha_chan = src_chans - 1;
420
421 while (work_width) {
422 if (*srcp) {
423 if (*srcp != IM_SAMPLE_MAX)
424 linep->channel[alpha_chan] =
425 linep->channel[alpha_chan] * *srcp / IM_SAMPLE_MAX;
426 }
427 else {
428 linep->channel[alpha_chan] = 0;
429 }
430 --work_width;
431 ++srcp;
432 ++linep;
433 }
434 }
435 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
436 combine(r->IM_SUFFIX(line), line, im->channels, width);
437 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
438 }
439 else {
440 if (src) {
50c75381 441 i_img_dim work_width = width;
9b1ec2b8
TC
442 IM_COLOR *srcc = line;
443 IM_COLOR *destc = r->IM_SUFFIX(line);
444
445 IM_GLIN(im, x, x+width, y, r->IM_SUFFIX(line));
446 while (work_width) {
447 if (*src == 255) {
448 /* just replace it */
449 *destc = *srcc;
450 }
451 else if (*src) {
452 int ch;
453 for (ch = 0; ch < im->channels; ++ch) {
454 IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src)
455 + srcc->channel[ch] * *src) / IM_SAMPLE_MAX;
456 destc->channel[ch] = IM_LIMIT(work);
457 }
458 }
459
460 ++srcc;
461 ++destc;
462 ++src;
463 --work_width;
464 }
465 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
466 }
467 else {
468 IM_PLIN(im, x, x+width, y, line);
469 }
470 }
471}
472
9c106321
TC
473static
474void
50c75381
TC
475IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y,
476 i_img_dim width, unsigned char const *src,
477 i_color const *color) {
9c106321
TC
478 i_img *im = r->im;
479 IM_COLOR *linep = r->IM_SUFFIX(line);
480 int ch, channels = im->channels;
50c75381 481 i_img_dim fetch_offset;
9c106321
TC
482#undef STORE_COLOR
483#ifdef IM_EIGHT_BIT
484#define STORE_COLOR (*color)
485#else
486 i_fcolor fcolor;
487
488 for (ch = 0; ch < channels; ++ch) {
489 fcolor.channel[ch] = color->channel[ch] / 255.0;
490 }
491#define STORE_COLOR fcolor
492#endif
493
494 fetch_offset = 0;
495 while (fetch_offset < width && *src == 0xFF) {
496 *linep++ = STORE_COLOR;
497 ++src;
498 ++fetch_offset;
499 }
500 IM_GLIN(im, x+fetch_offset, x+width, y, linep);
501 while (fetch_offset < width) {
502#ifdef IM_EIGHT_BIT
503 IM_WORK_T alpha = *src++;
504#else
505 IM_WORK_T alpha = *src++ / 255.0;
506#endif
507 if (alpha == IM_SAMPLE_MAX)
508 *linep = STORE_COLOR;
509 else if (alpha) {
510 for (ch = 0; ch < channels; ++ch) {
511 linep->channel[ch] = (linep->channel[ch] * (IM_SAMPLE_MAX - alpha)
512 + STORE_COLOR.channel[ch] * alpha) / IM_SAMPLE_MAX;
513 }
514 }
515 ++linep;
516 ++fetch_offset;
517 }
518 IM_PLIN(im, x, x+width, y, r->IM_SUFFIX(line));
519}
520
521static
522void
50c75381
TC
523IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
524 i_img_dim width, unsigned char const *src,
525 i_color const *color) {
9c106321
TC
526 IM_COLOR *linep = r->IM_SUFFIX(line);
527 int ch;
528 int alpha_channel = r->im->channels - 1;
50c75381 529 i_img_dim fetch_offset;
9c106321
TC
530#undef STORE_COLOR
531#ifdef IM_EIGHT_BIT
532#define STORE_COLOR (*color)
533#else
534 i_fcolor fcolor;
535
536 for (ch = 0; ch < r->im->channels; ++ch) {
537 fcolor.channel[ch] = color->channel[ch] / 255.0;
538 }
539#define STORE_COLOR fcolor
540#endif
541
542 fetch_offset = 0;
543 while (fetch_offset < width && *src == 0xFF) {
544 *linep++ = STORE_COLOR;
545 ++src;
546 ++fetch_offset;
547 }
548 IM_GLIN(r->im, x+fetch_offset, x+width, y, linep);
549 while (fetch_offset < width) {
550#ifdef IM_EIGHT_BIT
551 IM_WORK_T src_alpha = *src++;
552#else
553 IM_WORK_T src_alpha = *src++ / 255.0;
554#endif
555 if (src_alpha == IM_SAMPLE_MAX)
556 *linep = STORE_COLOR;
557 else if (src_alpha) {
e6e94ab0 558 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
9c106321
TC
559 IM_WORK_T orig_alpha = linep->channel[alpha_channel];
560 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
561 for (ch = 0; ch < alpha_channel; ++ch) {
562 linep->channel[ch] = ( src_alpha * STORE_COLOR.channel[ch]
563 + remains * linep->channel[ch] * orig_alpha / IM_SAMPLE_MAX
564 ) / dest_alpha;
565 }
566 linep->channel[alpha_channel] = dest_alpha;
567 }
568 ++linep;
569 ++fetch_offset;
570 }
571 IM_PLIN(r->im, x, x+width, y, r->IM_SUFFIX(line));
9b1ec2b8
TC
572#undef STORE_COLOR
573}
574
575/* combine a line of image data with an output line, both the input
576 and output lines include an alpha channel.
577
578 Both input and output lines have I<channels> of data, channels
579 should be either 2 or 4.
580*/
581
582static void
583IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
50c75381 584 int channels, i_img_dim count) {
9b1ec2b8
TC
585 int ch;
586 int alpha_channel = channels - 1;
587
588 while (count) {
589 IM_WORK_T src_alpha = in->channel[alpha_channel];
590
591 if (src_alpha == IM_SAMPLE_MAX)
592 *out = *in;
593 else if (src_alpha) {
594 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
595 IM_WORK_T orig_alpha = out->channel[alpha_channel];
596 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
597
598 for (ch = 0; ch < alpha_channel; ++ch) {
599 out->channel[ch] = ( src_alpha * in->channel[ch]
600 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
601 ) / dest_alpha;
602 }
603 out->channel[alpha_channel] = dest_alpha;
604 }
605
606 ++out;
607 ++in;
608 --count;
609 }
610}
611
612/* combine a line of image data with an output line. The input line
613 includes an alpha channel, the output line has no alpha channel.
614
615 The input line has I<channels>+1 of color data. The output line
616 has I<channels> of color data.
617*/
618
619static void
620IM_SUFFIX(combine_line_noalpha)
50c75381 621 (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
9b1ec2b8
TC
622 int ch;
623
624 while (count) {
625 IM_WORK_T src_alpha = in->channel[channels];
626
627 if (src_alpha == IM_SAMPLE_MAX)
628 *out = *in;
629 else if (src_alpha) {
630 IM_WORK_T remains;
631
632 remains = IM_SAMPLE_MAX - src_alpha;
633 for (ch = 0; ch < channels; ++ch) {
634 out->channel[ch] = ( in->channel[ch] * src_alpha
635 + out->channel[ch] * remains) / IM_SAMPLE_MAX;
636 }
637 }
638
639 ++out;
640 ++in;
641 --count;
642 }
643}
644
645/* combine a line of image data with an output line, both the input
646 and output lines include an alpha channel.
647
648 Both input and output lines have I<channels> of data, channels
649 should be either 2 or 4.
650
651 This variant does not modify the output alpha channel.
652*/
653
654static void
655IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
50c75381 656 int channels, i_img_dim count) {
9b1ec2b8
TC
657 int ch;
658 int alpha_channel = channels - 1;
659
660 while (count) {
661 IM_WORK_T src_alpha = in->channel[alpha_channel];
662
663 if (src_alpha == IM_SAMPLE_MAX)
664 *out = *in;
665 else if (src_alpha) {
666 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
667 IM_WORK_T orig_alpha = out->channel[alpha_channel];
668 IM_WORK_T dest_alpha = src_alpha + (remains * orig_alpha) / IM_SAMPLE_MAX;
669
670 for (ch = 0; ch < alpha_channel; ++ch) {
671 out->channel[ch] = ( src_alpha * in->channel[ch]
672 + remains * out->channel[ch] * orig_alpha / IM_SAMPLE_MAX
673 ) / dest_alpha;
674 }
675 }
676
677 ++out;
678 ++in;
679 --count;
680 }
681}
682
683static void
50c75381 684IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
9b1ec2b8
TC
685 if (channels == 2 || channels == 4)
686 IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
687 else
688 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
689}
690
691static void
50c75381 692IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
9b1ec2b8
TC
693 if (channels == 2 || channels == 4)
694 IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
695 else
696 IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
697}
698
50c75381
TC
699static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
700static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
701static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
702static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
703static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
704static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
705static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
706static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
707static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
708static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
709static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
710static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
9b1ec2b8
TC
711
712static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
713{
714 NULL,
715 IM_SUFFIX(combine_alphablend),
716 IM_SUFFIX(combine_mult),
717 IM_SUFFIX(combine_dissolve),
718 IM_SUFFIX(combine_add),
719 IM_SUFFIX(combine_subtract),
720 IM_SUFFIX(combine_diff),
721 IM_SUFFIX(combine_lighten),
722 IM_SUFFIX(combine_darken),
723 IM_SUFFIX(combine_hue),
724 IM_SUFFIX(combine_sat),
725 IM_SUFFIX(combine_value),
726 IM_SUFFIX(combine_color)
727};
728
729#/code
730
731/*
732=item i_get_combine(combine, color_func, fcolor_func)
733
734=cut
735*/
736
737void i_get_combine(int combine, i_fill_combine_f *color_func,
738 i_fill_combinef_f *fcolor_func) {
739 if (combine < 0 || combine > sizeof(combines_8) / sizeof(*combines_8))
740 combine = 0;
741
742 *color_func = combines_8[combine];
743 *fcolor_func = combines_double[combine];
744}
745
746#code
747
748/*
749 Three good references for implementing combining modes:
750
751 http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html
752 referenced as [svg1.2]
753
754 http://gimp-savvy.com/BOOK/index.html?node55.html
755 ("The Blending Modes", if it changes)
756 referenced as [savvy]
757
758 http://www.pegtop.net/delphi/articles/blendmodes/
759 referenced as [pegtop]
760
761 Where differences exist, I follow the SVG practice, the gimp
762 practice, and lastly pegtop.
763*/
764
765
766static void
50c75381 767IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
768 IM_SUFFIX(combine_line)(out, in, channels, count);
769}
770
771/*
772Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
773Da' = Sa.Da + Sa.(1 - Da) + Da.(1 - Sa)
774 = Sa + Da - Sa.Da
775
776When Da=1
777
778Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
779 */
780static void
50c75381 781IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
782 int ch;
783 IM_COLOR *inp = in;
784 IM_COLOR *outp = out;
50c75381 785 i_img_dim work_count = count;
9b1ec2b8
TC
786 int color_channels = i_color_channels(channels);
787
788 if (i_has_alpha(channels)) {
789 while (work_count--) {
790 IM_WORK_T orig_alpha = outp->channel[color_channels];
791 IM_WORK_T src_alpha = inp->channel[color_channels];
792
793 if (src_alpha) {
794 IM_WORK_T dest_alpha = src_alpha + orig_alpha
795 - (src_alpha * orig_alpha) / IM_SAMPLE_MAX;
796
797 for (ch = 0; ch < color_channels; ++ch) {
798 outp->channel[ch] =
799 (inp->channel[ch] * src_alpha * outp->channel[ch] / IM_SAMPLE_MAX
800 * orig_alpha
801 + inp->channel[ch] * src_alpha * (IM_SAMPLE_MAX - orig_alpha)
802 + outp->channel[ch] * orig_alpha * (IM_SAMPLE_MAX - src_alpha))
803 / IM_SAMPLE_MAX / dest_alpha;
804 }
805 outp->channel[color_channels] = dest_alpha;
806 }
807 ++outp;
808 ++inp;
809 }
810 }
811 else {
812 while (work_count--) {
813 IM_WORK_T src_alpha = inp->channel[color_channels];
814 IM_WORK_T remains = IM_SAMPLE_MAX - src_alpha;
815
816 if (src_alpha) {
817 for (ch = 0; ch < color_channels; ++ch) {
818 outp->channel[ch] =
819 (src_alpha * inp->channel[ch] * outp->channel[ch] / IM_SAMPLE_MAX
820 + outp->channel[ch] * remains) / IM_SAMPLE_MAX;
821 }
822 }
823 ++outp;
824 ++inp;
825 }
826 }
827}
828
829static void
50c75381 830IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
831 int color_channels = i_color_channels(channels);
832 int ch;
833
834 if (i_has_alpha(channels)) {
835 while (count--) {
836 if (in->channel[channels-1] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
837 for (ch = 0; ch < color_channels; ++ch) {
838 out->channel[ch] = in->channel[ch];
839 }
840 out->channel[color_channels] = IM_SAMPLE_MAX;
841 }
842 ++out;
843 ++in;
844 }
845 }
846 else {
847 while (count--) {
848 if (in->channel[channels] > rand() * ((double)IM_SAMPLE_MAX / RAND_MAX)) {
849 for (ch = 0; ch < color_channels; ++ch) {
850 out->channel[ch] = in->channel[ch];
851 }
852 }
853 ++out;
854 ++in;
855 }
856 }
857}
858
859/*
860Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
861 = Sca + Dca
862Da' = Sa.Da + Da.Sa + Sa.(1 - Da) + Da.(1 - Sa)
863 = Sa + Da
864*/
865
866static void
50c75381 867IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
868 int ch;
869 int color_channels = i_color_channels(channels);
50c75381 870 i_img_dim work_count = count;
9b1ec2b8
TC
871 IM_COLOR *inp = in;
872 IM_COLOR *outp = out;
873
874 if (i_has_alpha(channels)) {
875 while (work_count--) {
876 IM_WORK_T src_alpha = inp->channel[color_channels];
877 if (src_alpha) {
878 IM_WORK_T orig_alpha = outp->channel[color_channels];
879 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
880 if (dest_alpha > IM_SAMPLE_MAX)
881 dest_alpha = IM_SAMPLE_MAX;
882 for (ch = 0; ch < color_channels; ++ch) {
883 IM_WORK_T total = (outp->channel[ch] * orig_alpha + inp->channel[ch] * src_alpha) / dest_alpha;
884 if (total > IM_SAMPLE_MAX)
885 total = IM_SAMPLE_MAX;
886 outp->channel[ch] = total;
887 }
888 outp->channel[color_channels] = dest_alpha;
889 }
890
891 ++outp;
892 ++inp;
893 }
894 }
895 else {
896 while (work_count--) {
897 IM_WORK_T src_alpha = inp->channel[color_channels];
898 if (src_alpha) {
899 for (ch = 0; ch < color_channels; ++ch) {
900 IM_WORK_T total = outp->channel[ch] + inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
901 if (total > IM_SAMPLE_MAX)
902 total = IM_SAMPLE_MAX;
903 outp->channel[ch] = total;
904 }
905 }
906
907 ++outp;
908 ++inp;
909 }
910 }
911}
912
913/*
914 [pegtop] documents this as max(A+B-256, 0) while [savvy] documents
915 it as max(A-B, 0). [svg1.2] doesn't cover it.
916
917 [savvy] doesn't document how it works with an alpha channel. GIMP
918 actually seems to calculate the final value then use the alpha
919 channel to apply that to the target.
920 */
921static void
50c75381 922IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
923 int ch;
924 IM_COLOR const *inp = in;
925 IM_COLOR *outp = out;
50c75381 926 i_img_dim work_count = count;
9b1ec2b8
TC
927 int color_channels = i_color_channels(channels);
928
929 if (i_has_alpha(channels)) {
930 while (work_count--) {
931 IM_WORK_T src_alpha = inp->channel[color_channels];
932 if (src_alpha) {
933 IM_WORK_T orig_alpha = outp->channel[color_channels];
934 IM_WORK_T dest_alpha = src_alpha + orig_alpha;
935 if (dest_alpha > IM_SAMPLE_MAX)
936 dest_alpha = IM_SAMPLE_MAX;
937 for (ch = 0; ch < color_channels; ++ch) {
938 IM_WORK_T total =
939 (outp->channel[ch] * orig_alpha - inp->channel[ch] * src_alpha)
940 / dest_alpha;
941 if (total < 0)
942 total = 0;
943 outp->channel[ch] = total;
944 }
945 outp->channel[color_channels] = dest_alpha;
946 }
947 ++outp;
948 ++inp;
949 }
950 }
951 else {
952 while (work_count--) {
953 IM_WORK_T src_alpha = inp->channel[color_channels];
954 if (src_alpha) {
955 for (ch = 0; ch < color_channels; ++ch) {
956 IM_WORK_T total = outp->channel[ch] - inp->channel[ch] * src_alpha / IM_SAMPLE_MAX;
957 if (total < 0)
958 total = 0;
959 outp->channel[ch] = total;
960 }
961 }
962 ++outp;
963 ++inp;
964 }
965 }
966}
967
968#ifdef IM_EIGHT_BIT
969#define IM_abs(x) abs(x)
970#else
971#define IM_abs(x) fabs(x)
972#endif
973
974/*
975Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
976 = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
977Da' = Sa + Da - Sa.Da
978*/
979static void
50c75381 980IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
981 int ch;
982 IM_COLOR const *inp = in;
983 IM_COLOR *outp = out;
50c75381 984 i_img_dim work_count = count;
9b1ec2b8
TC
985 int color_channels = i_color_channels(channels);
986
987 if (i_has_alpha(channels)) {
988 while (work_count--) {
989 IM_WORK_T src_alpha = inp->channel[color_channels];
990 if (src_alpha) {
991 IM_WORK_T orig_alpha = outp->channel[color_channels];
992 IM_WORK_T dest_alpha = src_alpha + orig_alpha
993 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
994 for (ch = 0; ch < color_channels; ++ch) {
995 IM_WORK_T src = inp->channel[ch] * src_alpha;
996 IM_WORK_T orig = outp->channel[ch] * orig_alpha;
997 IM_WORK_T src_da = src * orig_alpha;
998 IM_WORK_T dest_sa = orig * src_alpha;
999 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1000 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / dest_alpha;
1001 }
1002 outp->channel[color_channels] = dest_alpha;
1003 }
1004 ++inp;
1005 ++outp;
1006 }
1007 }
1008 else {
1009 while (work_count--) {
1010 IM_WORK_T src_alpha = inp->channel[color_channels];
1011 if (src_alpha) {
1012 for (ch = 0; ch < color_channels; ++ch) {
1013 IM_WORK_T src = inp->channel[ch] * src_alpha;
1014 IM_WORK_T orig = outp->channel[ch] * IM_SAMPLE_MAX;
1015 IM_WORK_T src_da = src * IM_SAMPLE_MAX;
1016 IM_WORK_T dest_sa = orig * src_alpha;
1017 IM_WORK_T diff = src_da < dest_sa ? src_da : dest_sa;
1018 outp->channel[ch] = (src + orig - 2 * diff / IM_SAMPLE_MAX) / IM_SAMPLE_MAX;
1019 }
1020 }
1021 ++inp;
1022 ++outp;
1023 }
1024 }
1025}
1026
1027#undef IM_abs
1028
1029/*
1030 Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca(1 - Sa)
1031 Da' = Sa + Da - Sa.Da
1032
1033 To hoist some code:
1034
1035 Dca' = min(Sc.Sa.Da, Dc.Da.Sa) + Sca - Sca.Da + Dca - Dca.Sa
1036 = Sa.Da.min(Sc, Dc) + Sca - Sca.Da + Dca - Dca.Sa
1037
1038 When Da=1:
1039
1040 Dca' = min(Sca.1, Dc.1.Sa) + Sca.(1 - 1) + Dc.1(1 - Sa)
1041 = min(Sca, Dc.Sa) + Dc(1-Sa)
1042 = Sa.min(Sc, Dc) + Dc - Dc.Sa
1043 Da' = Sa + 1 - Sa.1
1044 = 1
1045 */
1046static void
1047IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
50c75381 1048 i_img_dim count) {
9b1ec2b8
TC
1049 int ch;
1050 IM_COLOR const *inp = in;
1051 IM_COLOR *outp = out;
50c75381 1052 i_img_dim work_count = count;
9b1ec2b8
TC
1053 int color_channels = i_color_channels(channels);
1054
1055 if (i_has_alpha(channels)) {
1056 while (work_count--) {
1057 IM_WORK_T src_alpha = inp->channel[color_channels];
1058
1059 if (src_alpha) {
1060 IM_WORK_T orig_alpha = outp->channel[color_channels];
1061 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1062 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1063 for (ch = 0; ch < color_channels; ++ch) {
1064 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1065 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1066 IM_WORK_T ScaDa = Sca * orig_alpha;
1067 IM_WORK_T DcaSa = Dca * src_alpha;
1068 IM_WORK_T minc = ScaDa < DcaSa ? ScaDa : DcaSa;
1069 outp->channel[ch] =
1070 (
1071 minc + (Sca + Dca) * IM_SAMPLE_MAX
1072 - ScaDa - DcaSa
1073 ) / (IM_SAMPLE_MAX * dest_alpha);
1074 }
1075 outp->channel[color_channels] = dest_alpha;
1076 }
1077 ++outp;
1078 ++inp;
1079 }
1080 }
1081 else {
1082 while (work_count--) {
1083 IM_WORK_T src_alpha = inp->channel[color_channels];
1084
1085 if (src_alpha) {
1086 for (ch = 0; ch < color_channels; ++ch) {
1087 IM_WORK_T minc = outp->channel[ch] < inp->channel[ch]
1088 ? outp->channel[ch] : inp->channel[ch];
1089 outp->channel[ch] =
1090 (
1091 src_alpha * minc +
1092 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1093 ) / IM_SAMPLE_MAX;
1094 }
1095 }
1096 ++outp;
1097 ++inp;
1098 }
1099 }
1100}
1101
1102static void
50c75381 1103IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1104 int ch;
1105 IM_COLOR const *inp = in;
1106 IM_COLOR *outp = out;
50c75381 1107 i_img_dim work_count = count;
9b1ec2b8
TC
1108 int color_channels = i_color_channels(channels);
1109
1110 if (i_has_alpha(channels)) {
1111 while (work_count--) {
1112 IM_WORK_T src_alpha = inp->channel[color_channels];
1113
1114 if (src_alpha) {
1115 IM_WORK_T orig_alpha = outp->channel[color_channels];
1116 IM_WORK_T dest_alpha = src_alpha + orig_alpha
1117 - src_alpha * orig_alpha / IM_SAMPLE_MAX;
1118 for (ch = 0; ch < color_channels; ++ch) {
1119 IM_WORK_T Sca = inp->channel[ch] * src_alpha;
1120 IM_WORK_T Dca = outp->channel[ch] * orig_alpha;
1121 IM_WORK_T ScaDa = Sca * orig_alpha;
1122 IM_WORK_T DcaSa = Dca * src_alpha;
1123 IM_WORK_T maxc = ScaDa > DcaSa ? ScaDa : DcaSa;
1124 outp->channel[ch] =
1125 (
1126 maxc + (Sca + Dca) * IM_SAMPLE_MAX
1127 - ScaDa - DcaSa
1128 ) / (IM_SAMPLE_MAX * dest_alpha);
1129 }
1130 outp->channel[color_channels] = dest_alpha;
1131 }
1132 ++outp;
1133 ++inp;
1134 }
1135 }
1136 else {
1137 while (work_count--) {
1138 IM_WORK_T src_alpha = inp->channel[color_channels];
1139
1140 if (src_alpha) {
1141 for (ch = 0; ch < color_channels; ++ch) {
1142 IM_WORK_T maxc = outp->channel[ch] > inp->channel[ch]
1143 ? outp->channel[ch] : inp->channel[ch];
1144 outp->channel[ch] =
1145 (
1146 src_alpha * maxc +
1147 outp->channel[ch] * ( IM_SAMPLE_MAX - src_alpha )
1148 ) / IM_SAMPLE_MAX;
1149 }
1150 }
1151 ++outp;
1152 ++inp;
1153 }
1154 }
1155}
1156
1157#if IM_EIGHT_BIT
1158#define IM_RGB_TO_HSV i_rgb_to_hsv
1159#define IM_HSV_TO_RGB i_hsv_to_rgb
1160#else
1161#define IM_RGB_TO_HSV i_rgb_to_hsvf
1162#define IM_HSV_TO_RGB i_hsv_to_rgbf
1163#endif
1164
1165static void
50c75381 1166IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1167 if (channels > 2) {
1168 IM_COLOR *inp = in;
1169 IM_COLOR const *outp = out;
50c75381 1170 i_img_dim work_count = count;
9b1ec2b8
TC
1171
1172 if (i_has_alpha(channels)) {
1173 while (work_count--) {
1174 IM_COLOR c = *inp;
1175 IM_RGB_TO_HSV(&c);
1176 /* only transfer hue if there's saturation */
1177 if (c.channel[1] && inp->channel[3] && outp->channel[3]) {
1178 *inp = *outp;
1179 IM_RGB_TO_HSV(inp);
1180 /* and no point in setting the target hue if the target has no sat */
1181 if (inp->channel[1]) {
1182 inp->channel[0] = c.channel[0];
1183 IM_HSV_TO_RGB(inp);
1184 inp->channel[3] = c.channel[3];
1185 }
1186 else {
1187 inp->channel[3] = 0;
1188 }
1189 }
1190 else {
1191 inp->channel[3] = 0;
1192 }
1193
1194 ++outp;
1195 ++inp;
1196 }
1197 }
1198 else {
1199 while (work_count--) {
1200 IM_COLOR c = *inp;
1201 IM_RGB_TO_HSV(&c);
1202 /* only transfer hue if there's saturation */
1203 if (c.channel[1] && inp->channel[3]) {
1204 *inp = *outp;
1205 IM_RGB_TO_HSV(inp);
1206 /* and no point in setting the target hue if the target has no sat */
1207 if (inp->channel[1]) {
1208 inp->channel[0] = c.channel[0];
1209 IM_HSV_TO_RGB(inp);
1210 inp->channel[3] = c.channel[3];
1211 }
1212 }
1213 else {
1214 inp->channel[3] = 0;
1215 }
1216
1217 ++outp;
1218 ++inp;
1219 }
1220 }
1221
1222 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1223 }
9c106321
TC
1224}
1225
9b1ec2b8 1226static void
50c75381 1227IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1228 if (channels > 2) {
1229 IM_COLOR *inp = in;
1230 IM_COLOR const *outp = out;
50c75381 1231 i_img_dim work_count = count;
9b1ec2b8
TC
1232
1233 while (work_count--) {
1234 IM_COLOR c = *inp;
1235 *inp = *outp;
1236 IM_RGB_TO_HSV(&c);
1237 IM_RGB_TO_HSV(inp);
1238 inp->channel[1] = c.channel[1];
1239 IM_HSV_TO_RGB(inp);
1240 inp->channel[3] = c.channel[3];
1241 ++outp;
1242 ++inp;
1243 }
1244
1245 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1246 }
1247}
1248
1249static void
50c75381 1250IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1251 if (channels > 2) {
1252 IM_COLOR *inp = in;
1253 IM_COLOR const *outp = out;
50c75381 1254 i_img_dim work_count = count;
9b1ec2b8
TC
1255
1256 while (work_count--) {
1257 IM_COLOR c = *inp;
1258 *inp = *outp;
1259 IM_RGB_TO_HSV(&c);
1260 IM_RGB_TO_HSV(inp);
1261 inp->channel[2] = c.channel[2];
1262 IM_HSV_TO_RGB(inp);
1263 inp->channel[3] = c.channel[3];
1264 ++outp;
1265 ++inp;
1266 }
1267 }
1268
1269 /* all images have a "value channel" - for greyscale it's the only
1270 colour channel */
1271 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1272}
1273
1274static void
50c75381 1275IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
9b1ec2b8
TC
1276 if (channels > 2) {
1277 IM_COLOR *inp = in;
1278 IM_COLOR const *outp = out;
50c75381 1279 i_img_dim work_count = count;
9b1ec2b8
TC
1280
1281 while (work_count--) {
1282 IM_COLOR c = *inp;
1283 *inp = *outp;
1284 IM_RGB_TO_HSV(&c);
1285 IM_RGB_TO_HSV(inp);
1286 inp->channel[0] = c.channel[0];
1287 inp->channel[1] = c.channel[1];
1288 IM_HSV_TO_RGB(inp);
1289 inp->channel[3] = c.channel[3];
1290 ++outp;
1291 ++inp;
1292 }
1293
1294 IM_SUFFIX(combine_line_na)(out, in, channels, count);
1295 }
1296}
1297
1298#undef IM_RGB_TO_HSV
1299#undef IM_HSV_TO_RGB
1300
9c106321 1301#/code