1 /*
2 * art_render.c: Modular rendering architecture.
3 *
4 * Libart_LGPL - library of basic graphic primitives
5 * Copyright (C) 2000 Raph Levien
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
23 #include "art_misc.h"
24 #include "art_alphagamma.h"
25 #include "art_rgb.h"
27 #include "art_render.h"
29 typedef struct _ArtRenderPriv ArtRenderPriv;
31 struct _ArtRenderPriv {
32 ArtRender super;
34 ArtImageSource *image_source;
36 int n_mask_source;
37 ArtMaskSource **mask_source;
39 int n_callbacks;
40 ArtRenderCallback **callbacks;
41 };
43 ArtRender *
44 art_render_new (int x0, int y0, int x1, int y1,
45 art_u8 *pixels, int rowstride,
46 int n_chan, int depth, ArtAlphaType alpha_type,
47 ArtAlphaGamma *alphagamma)
48 {
49 ArtRenderPriv *priv;
50 ArtRender *result;
52 priv = art_new (ArtRenderPriv, 1);
53 result = &priv->super;
55 if (n_chan > ART_MAX_CHAN)
56 {
57 art_warn ("art_render_new: n_chan = %d, exceeds %d max\n",
58 n_chan, ART_MAX_CHAN);
59 return NULL;
60 }
61 if (depth > ART_MAX_DEPTH)
62 {
63 art_warn ("art_render_new: depth = %d, exceeds %d max\n",
64 depth, ART_MAX_DEPTH);
65 return NULL;
66 }
67 if (x0 >= x1)
68 {
69 art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1);
70 return NULL;
71 }
72 result->x0 = x0;
73 result->y0 = y0;
74 result->x1 = x1;
75 result->y1 = y1;
76 result->pixels = pixels;
77 result->rowstride = rowstride;
78 result->n_chan = n_chan;
79 result->depth = depth;
80 result->alpha_type = alpha_type;
82 result->clear = ART_FALSE;
83 result->opacity = 0x10000;
84 result->compositing_mode = ART_COMPOSITE_NORMAL;
85 result->alphagamma = alphagamma;
87 result->alpha_buf = NULL;
88 result->image_buf = NULL;
90 result->run = NULL;
91 result->span_x = NULL;
93 result->need_span = ART_FALSE;
95 priv->image_source = NULL;
97 priv->n_mask_source = 0;
98 priv->mask_source = NULL;
100 return result;
101 }
103 /* todo on clear routines: I haven't really figured out what to do
104 with clearing the alpha channel. It _should_ be possible to clear
105 to an arbitrary RGBA color. */
107 /**
108 * art_render_clear: Set clear color.
109 * @clear_color: Color with which to clear dest.
110 *
111 * Sets clear color, equivalent to actually clearing the destination
112 * buffer before rendering. This is the most general form.
113 **/
114 void
115 art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color)
116 {
117 int i;
118 int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
120 render->clear = ART_TRUE;
121 for (i = 0; i < n_ch; i++)
122 render->clear_color[i] = clear_color[i];
123 }
125 /**
126 * art_render_clear_rgb: Set clear color, given in RGB format.
127 * @clear_rgb: Clear color, in 0xRRGGBB format.
128 *
129 * Sets clear color, equivalent to actually clearing the destination
130 * buffer before rendering.
131 **/
132 void
133 art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb)
134 {
135 if (render->n_chan != 3)
136 art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n",
137 render->n_chan);
138 else
139 {
140 int r, g, b;
142 render->clear = ART_TRUE;
143 r = clear_rgb >> 16;
144 g = (clear_rgb >> 8) & 0xff;
145 b = clear_rgb & 0xff;
146 render->clear_color[0] = ART_PIX_MAX_FROM_8(r);
147 render->clear_color[1] = ART_PIX_MAX_FROM_8(g);
148 render->clear_color[2] = ART_PIX_MAX_FROM_8(b);
149 }
150 }
152 static void
153 art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
154 {
155 }
157 static void
158 art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render,
159 art_u8 *dest, int y)
160 {
161 int width = render->x1 - render->x0;
162 art_u8 r, g, b;
163 ArtPixMaxDepth color_max;
165 color_max = render->clear_color[0];
166 r = ART_PIX_8_FROM_MAX (color_max);
167 color_max = render->clear_color[1];
168 g = ART_PIX_8_FROM_MAX (color_max);
169 color_max = render->clear_color[2];
170 b = ART_PIX_8_FROM_MAX (color_max);
172 art_rgb_fill_run (dest, r, g, b, width);
173 }
175 static void
176 art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render,
177 art_u8 *dest, int y)
178 {
179 int width = render->x1 - render->x0;
180 int i, j;
181 int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
182 int ix;
183 art_u8 color[ART_MAX_CHAN + 1];
185 for (j = 0; j < n_ch; j++)
186 {
187 ArtPixMaxDepth color_max = render->clear_color[j];
188 color[j] = ART_PIX_8_FROM_MAX (color_max);
189 }
191 ix = 0;
192 for (i = 0; i < width; i++)
193 for (j = 0; j < n_ch; j++)
194 dest[ix++] = color[j];
195 }
197 const ArtRenderCallback art_render_clear_rgb8_obj =
198 {
199 art_render_clear_render_rgb8,
200 art_render_nop_done
201 };
203 const ArtRenderCallback art_render_clear_8_obj =
204 {
205 art_render_clear_render_8,
206 art_render_nop_done
207 };
209 #if ART_MAX_DEPTH >= 16
211 static void
212 art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render,
213 art_u8 *dest, int y)
214 {
215 int width = render->x1 - render->x0;
216 int i, j;
217 int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
218 int ix;
219 art_u16 *dest_16 = (art_u16 *)dest;
220 art_u8 color[ART_MAX_CHAN + 1];
222 for (j = 0; j < n_ch; j++)
223 {
224 int color_16 = render->clear_color[j];
225 color[j] = color_16;
226 }
228 ix = 0;
229 for (i = 0; i < width; i++)
230 for (j = 0; j < n_ch; j++)
231 dest_16[ix++] = color[j];
232 }
234 const ArtRenderCallback art_render_clear_16_obj =
235 {
236 art_render_clear_render_16,
237 art_render_nop_done
238 };
240 #endif /* ART_MAX_DEPTH >= 16 */
242 /* todo: inline */
243 static ArtRenderCallback *
244 art_render_choose_clear_callback (ArtRender *render)
245 {
246 ArtRenderCallback *clear_callback;
248 if (render->depth == 8)
249 {
250 if (render->n_chan == 3 &&
251 render->alpha_type == ART_ALPHA_NONE)
252 clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj;
253 else
254 clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj;
255 }
256 #if ART_MAX_DEPTH >= 16
257 else if (render->depth == 16)
258 clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj;
259 #endif
260 else
261 {
262 art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n",
263 render->depth);
264 }
265 return clear_callback;
266 }
268 #if 0
269 /* todo: get around to writing this */
270 static void
271 art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render,
272 art_u8 *dest, int y)
273 {
274 int width = render->x1 - render->x0;
276 }
277 #endif
279 /* This is the most general form of the function. It is slow but
280 (hopefully) correct. Actually, I'm still worried about roundoff
281 errors in the premul case - it seems to me that an off-by-one could
282 lead to overflow. */
283 static void
284 art_render_composite (ArtRenderCallback *self, ArtRender *render,
285 art_u8 *dest, int y)
286 {
287 ArtRenderMaskRun *run = render->run;
288 art_u32 depth = render->depth;
289 int n_run = render->n_run;
290 int x0 = render->x0;
291 int x;
292 int run_x0, run_x1;
293 art_u8 *alpha_buf = render->alpha_buf;
294 art_u8 *image_buf = render->image_buf;
295 int i, j;
296 art_u32 tmp;
297 art_u32 run_alpha;
298 art_u32 alpha;
299 int image_ix;
300 art_u16 src[ART_MAX_CHAN + 1];
301 art_u16 dst[ART_MAX_CHAN + 1];
302 int n_chan = render->n_chan;
303 ArtAlphaType alpha_type = render->alpha_type;
304 int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
305 int dst_pixstride = n_ch * (depth >> 3);
306 int buf_depth = render->buf_depth;
307 ArtAlphaType buf_alpha = render->buf_alpha;
308 int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
309 int buf_pixstride = buf_n_ch * (buf_depth >> 3);
310 art_u8 *bufptr;
311 art_u32 src_alpha;
312 art_u32 src_mul;
313 art_u8 *dstptr;
314 art_u32 dst_alpha;
315 art_u32 dst_mul;
317 image_ix = 0;
318 for (i = 0; i < n_run - 1; i++)
319 {
320 run_x0 = run[i].x;
321 run_x1 = run[i + 1].x;
322 tmp = run[i].alpha;
323 if (tmp < 0x8100)
324 continue;
326 run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
327 bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
328 dstptr = dest + (run_x0 - x0) * dst_pixstride;
329 for (x = run_x0; x < run_x1; x++)
330 {
331 if (alpha_buf)
332 {
333 if (depth == 8)
334 {
335 tmp = run_alpha * alpha_buf[x - x0] + 0x80;
336 /* range 0x80 .. 0xff0080 */
337 alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
338 }
339 else /* (depth == 16) */
340 {
341 tmp = ((art_u16 *)alpha_buf)[x - x0];
342 tmp = (run_alpha * tmp + 0x8000) >> 8;
343 /* range 0x80 .. 0xffff80 */
344 alpha = (tmp + (tmp >> 16)) >> 8;
345 }
346 }
347 else
348 alpha = run_alpha;
349 /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
351 /* convert (src pixel * alpha) to premul alpha form,
352 store in src as 0..0xffff range */
353 if (buf_alpha == ART_ALPHA_NONE)
354 {
355 src_alpha = alpha;
356 src_mul = src_alpha;
357 }
358 else
359 {
360 if (buf_depth == 8)
361 {
362 tmp = alpha * bufptr[n_chan] + 0x80;
363 /* range 0x80 .. 0xff0080 */
364 src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
365 }
366 else /* (depth == 16) */
367 {
368 tmp = ((art_u16 *)bufptr)[n_chan];
369 tmp = (alpha * tmp + 0x8000) >> 8;
370 /* range 0x80 .. 0xffff80 */
371 src_alpha = (tmp + (tmp >> 16)) >> 8;
372 }
373 if (buf_alpha == ART_ALPHA_SEPARATE)
374 src_mul = src_alpha;
375 else /* buf_alpha == (ART_ALPHA_PREMUL) */
376 src_mul = alpha;
377 }
378 /* src_alpha is the (alpha of the source pixel * alpha),
379 range 0..0x10000 */
381 if (buf_depth == 8)
382 {
383 src_mul *= 0x101;
384 for (j = 0; j < n_chan; j++)
385 src[j] = (bufptr[j] * src_mul + 0x8000) >> 16;
386 }
387 else if (buf_depth == 16)
388 {
389 for (j = 0; j < n_chan; j++)
390 src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16;
391 }
392 bufptr += buf_pixstride;
394 /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range
395 0..0x10000) now contain the source pixel with
396 premultiplied alpha */
398 /* convert dst pixel to premul alpha form,
399 store in dst as 0..0xffff range */
400 if (alpha_type == ART_ALPHA_NONE)
401 {
402 dst_alpha = 0x10000;
403 dst_mul = dst_alpha;
404 }
405 else
406 {
407 if (depth == 8)
408 {
409 tmp = dstptr[n_chan];
410 /* range 0..0xff */
411 dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
412 }
413 else /* (depth == 16) */
414 {
415 tmp = ((art_u16 *)bufptr)[n_chan];
416 dst_alpha = (tmp + (tmp >> 15));
417 }
418 if (alpha_type == ART_ALPHA_SEPARATE)
419 dst_mul = dst_alpha;
420 else /* (alpha_type == ART_ALPHA_PREMUL) */
421 dst_mul = 0x10000;
422 }
423 /* dst_alpha is the alpha of the dest pixel,
424 range 0..0x10000 */
426 if (depth == 8)
427 {
428 dst_mul *= 0x101;
429 for (j = 0; j < n_chan; j++)
430 dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16;
431 }
432 else if (buf_depth == 16)
433 {
434 for (j = 0; j < n_chan; j++)
435 dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16;
436 }
438 /* do the compositing, dst = (src over dst) */
439 for (j = 0; j < n_chan; j++)
440 {
441 art_u32 srcv, dstv;
442 art_u32 tmp;
444 srcv = src[j];
445 dstv = dst[j];
446 tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv;
447 tmp -= tmp >> 16;
448 dst[j] = tmp;
449 }
451 if (alpha_type == ART_ALPHA_NONE)
452 {
453 if (depth == 8)
454 dst_mul = 0xff;
455 else /* (depth == 16) */
456 dst_mul = 0xffff;
457 }
458 else
459 {
460 if (src_alpha >= 0x10000)
461 dst_alpha = 0x10000;
462 else
463 dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
464 if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
465 {
466 if (depth == 8)
467 dst_mul = 0xff;
468 else /* (depth == 16) */
469 dst_mul = 0xffff;
470 }
471 else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
472 {
473 if (depth == 8)
474 dst_mul = 0xff0000 / dst_alpha;
475 else /* (depth == 16) */
476 dst_mul = 0xffff0000 / dst_alpha;
477 }
478 }
479 if (depth == 8)
480 {
481 for (j = 0; j < n_chan; j++)
482 dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16;
483 if (alpha_type != ART_ALPHA_NONE)
484 dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
485 }
486 else if (depth == 16)
487 {
488 for (j = 0; j < n_chan; j++)
489 ((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16;
490 if (alpha_type != ART_ALPHA_NONE)
491 dstptr[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16;
492 }
493 dstptr += dst_pixstride;
494 }
495 }
496 }
498 const ArtRenderCallback art_render_composite_obj =
499 {
500 art_render_composite,
501 art_render_nop_done
502 };
504 static void
505 art_render_composite_8 (ArtRenderCallback *self, ArtRender *render,
506 art_u8 *dest, int y)
507 {
508 ArtRenderMaskRun *run = render->run;
509 int n_run = render->n_run;
510 int x0 = render->x0;
511 int x;
512 int run_x0, run_x1;
513 art_u8 *alpha_buf = render->alpha_buf;
514 art_u8 *image_buf = render->image_buf;
515 int i, j;
516 art_u32 tmp;
517 art_u32 run_alpha;
518 art_u32 alpha;
519 int image_ix;
520 int n_chan = render->n_chan;
521 ArtAlphaType alpha_type = render->alpha_type;
522 int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
523 int dst_pixstride = n_ch;
524 ArtAlphaType buf_alpha = render->buf_alpha;
525 int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
526 int buf_pixstride = buf_n_ch;
527 art_u8 *bufptr;
528 art_u32 src_alpha;
529 art_u32 src_mul;
530 art_u8 *dstptr;
531 art_u32 dst_alpha;
532 art_u32 dst_mul, dst_save_mul;
534 image_ix = 0;
535 for (i = 0; i < n_run - 1; i++)
536 {
537 run_x0 = run[i].x;
538 run_x1 = run[i + 1].x;
539 tmp = run[i].alpha;
540 if (tmp < 0x10000)
541 continue;
543 run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
544 bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
545 dstptr = dest + (run_x0 - x0) * dst_pixstride;
546 for (x = run_x0; x < run_x1; x++)
547 {
548 if (alpha_buf)
549 {
550 tmp = run_alpha * alpha_buf[x - x0] + 0x80;
551 /* range 0x80 .. 0xff0080 */
552 alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
553 }
554 else
555 alpha = run_alpha;
556 /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
558 /* convert (src pixel * alpha) to premul alpha form,
559 store in src as 0..0xffff range */
560 if (buf_alpha == ART_ALPHA_NONE)
561 {
562 src_alpha = alpha;
563 src_mul = src_alpha;
564 }
565 else
566 {
567 tmp = alpha * bufptr[n_chan] + 0x80;
568 /* range 0x80 .. 0xff0080 */
569 src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
571 if (buf_alpha == ART_ALPHA_SEPARATE)
572 src_mul = src_alpha;
573 else /* buf_alpha == (ART_ALPHA_PREMUL) */
574 src_mul = alpha;
575 }
576 /* src_alpha is the (alpha of the source pixel * alpha),
577 range 0..0x10000 */
579 src_mul *= 0x101;
581 if (alpha_type == ART_ALPHA_NONE)
582 {
583 dst_alpha = 0x10000;
584 dst_mul = dst_alpha;
585 }
586 else
587 {
588 tmp = dstptr[n_chan];
589 /* range 0..0xff */
590 dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
591 if (alpha_type == ART_ALPHA_SEPARATE)
592 dst_mul = dst_alpha;
593 else /* (alpha_type == ART_ALPHA_PREMUL) */
594 dst_mul = 0x10000;
595 }
596 /* dst_alpha is the alpha of the dest pixel,
597 range 0..0x10000 */
599 dst_mul *= 0x101;
601 if (alpha_type == ART_ALPHA_NONE)
602 {
603 dst_save_mul = 0xff;
604 }
605 else
606 {
607 if (src_alpha >= 0x10000)
608 dst_alpha = 0x10000;
609 else
610 dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
611 if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
612 {
613 dst_save_mul = 0xff;
614 }
615 else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
616 {
617 dst_save_mul = 0xff0000 / dst_alpha;
618 }
619 }
620 for (j = 0; j < n_chan; j++)
621 {
622 art_u32 src, dst;
623 art_u32 tmp;
625 src = (bufptr[j] * src_mul + 0x8000) >> 16;
626 dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
627 tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
628 tmp -= tmp >> 16;
629 dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
630 }
631 if (alpha_type != ART_ALPHA_NONE)
632 dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
634 bufptr += buf_pixstride;
635 dstptr += dst_pixstride;
636 }
637 }
638 }
640 const ArtRenderCallback art_render_composite_8_obj =
641 {
642 art_render_composite_8,
643 art_render_nop_done
644 };
646 /* todo: inline */
647 static ArtRenderCallback *
648 art_render_choose_compositing_callback (ArtRender *render)
649 {
650 if (render->depth == 8 && render->buf_depth == 8)
651 return (ArtRenderCallback *)&art_render_composite_8_obj;
652 return (ArtRenderCallback *)&art_render_composite_obj;
653 }
655 /**
656 * art_render_invoke_callbacks: Invoke the callbacks in the render object.
657 * @render: The render object.
658 * @y: The current Y coordinate value.
659 *
660 * Invokes the callbacks of the render object in the appropriate
661 * order. Drivers should call this routine once per scanline.
662 *
663 * todo: should management of dest devolve to this routine? very
664 * plausibly yes.
665 **/
666 void
667 art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y)
668 {
669 ArtRenderPriv *priv = (ArtRenderPriv *)render;
670 int i;
672 for (i = 0; i < priv->n_callbacks; i++)
673 {
674 ArtRenderCallback *callback;
676 callback = priv->callbacks[i];
677 callback->render (callback, render, dest, y);
678 }
679 }
681 /**
682 * art_render_invoke: Perform the requested rendering task.
683 * @render: The render object.
684 *
685 * Invokes the renderer and all sources associated with it, to perform
686 * the requested rendering task.
687 **/
688 void
689 art_render_invoke (ArtRender *render)
690 {
691 ArtRenderPriv *priv = (ArtRenderPriv *)render;
692 int width;
693 int best_driver, best_score;
694 int i;
695 int n_callbacks, n_callbacks_max;
696 ArtImageSource *image_source;
697 ArtImageSourceFlags image_flags;
698 int buf_depth;
699 ArtAlphaType buf_alpha;
700 art_boolean first = ART_TRUE;
702 if (render == NULL)
703 {
704 art_warn ("art_render_invoke: called with render == NULL\n");
705 return;
706 }
707 if (priv->image_source == NULL)
708 {
709 art_warn ("art_render_invoke: no image source given\n");
710 return;
711 }
713 width = render->x1 - render->x0;
715 render->run = art_new (ArtRenderMaskRun, width + 1);
717 /* Elect a mask source as driver. */
718 best_driver = -1;
719 best_score = 0;
720 for (i = 0; i < priv->n_mask_source; i++)
721 {
722 int score;
723 ArtMaskSource *mask_source;
725 mask_source = priv->mask_source[i];
726 score = mask_source->can_drive (mask_source, render);
727 if (score > best_score)
728 {
729 best_score = score;
730 best_driver = i;
731 }
732 }
734 /* Allocate alpha buffer if needed. */
735 if (priv->n_mask_source > 1 ||
736 (priv->n_mask_source == 1 && best_driver < 0))
737 {
738 render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3);
739 }
741 /* Negotiate image rendering and compositing. */
742 image_source = priv->image_source;
743 image_source->negotiate (image_source, render, &image_flags, &buf_depth,
744 &buf_alpha);
746 /* Build callback list. */
747 n_callbacks_max = priv->n_mask_source + 3;
748 priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max);
749 n_callbacks = 0;
750 for (i = 0; i < priv->n_mask_source; i++)
751 if (i != best_driver)
752 {
753 ArtMaskSource *mask_source = priv->mask_source[i];
755 mask_source->prepare (mask_source, render, first);
756 first = ART_FALSE;
757 priv->callbacks[n_callbacks++] = &mask_source->super;
758 }
760 if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR))
761 priv->callbacks[n_callbacks++] =
762 art_render_choose_clear_callback (render);
764 priv->callbacks[n_callbacks++] = &image_source->super;
766 /* Allocate image buffer and add compositing callback if needed. */
767 if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE))
768 {
769 int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) *
770 buf_depth) >> 3;
771 render->buf_depth = buf_depth;
772 render->buf_alpha = buf_alpha;
773 render->image_buf = art_new (art_u8, width * bytespp);
774 priv->callbacks[n_callbacks++] =
775 art_render_choose_compositing_callback (render);
776 }
778 priv->n_callbacks = n_callbacks;
780 if (render->need_span)
781 render->span_x = art_new (int, width + 1);
783 /* Invoke the driver */
784 if (best_driver >= 0)
785 {
786 ArtMaskSource *driver;
788 driver = priv->mask_source[best_driver];
789 driver->invoke_driver (driver, render);
790 }
791 else
792 {
793 art_u8 *dest_ptr = render->pixels;
794 int y;
796 /* Dummy driver */
797 render->n_run = 2;
798 render->run[0].x = render->x0;
799 render->run[0].alpha = 0x8000 + 0xff * render->opacity;
800 render->run[1].x = render->x1;
801 render->run[1].alpha = 0x8000;
802 if (render->need_span)
803 {
804 render->n_span = 2;
805 render->span_x[0] = render->x0;
806 render->span_x[1] = render->x1;
807 }
808 for (y = render->y0; y < render->y1; y++)
809 {
810 art_render_invoke_callbacks (render, dest_ptr, y);
811 dest_ptr += render->rowstride;
812 }
813 }
815 if (priv->mask_source != NULL)
816 art_free (priv->mask_source);
818 /* clean up callbacks */
819 for (i = 0; i < priv->n_callbacks; i++)
820 {
821 ArtRenderCallback *callback;
823 callback = priv->callbacks[i];
824 callback->done (callback, render);
825 }
827 /* Tear down object */
828 if (render->alpha_buf != NULL)
829 art_free (render->alpha_buf);
830 if (render->image_buf != NULL)
831 art_free (render->image_buf);
832 art_free (render->run);
833 if (render->span_x != NULL)
834 art_free (render->span_x);
835 art_free (priv->callbacks);
836 art_free (render);
837 }
839 /**
840 * art_render_mask_solid: Add a solid translucent mask.
841 * @render: The render object.
842 * @opacity: Opacity in [0..0x10000] form.
843 *
844 * Adds a translucent mask to the rendering object.
845 **/
846 void
847 art_render_mask_solid (ArtRender *render, int opacity)
848 {
849 art_u32 old_opacity = render->opacity;
850 art_u32 new_opacity_tmp;
852 if (opacity == 0x10000)
853 /* avoid potential overflow */
854 return;
855 new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000;
856 render->opacity = new_opacity_tmp >> 16;
857 }
859 /**
860 * art_render_add_mask_source: Add a mask source to the render object.
861 * @render: Render object.
862 * @mask_source: Mask source to add.
863 *
864 * This routine adds a mask source to the render object. In general,
865 * client api's for adding mask sources should just take a render object,
866 * then the mask source creation function should call this function.
867 * Clients should never have to call this function directly, unless of
868 * course they're creating custom mask sources.
869 **/
870 void
871 art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source)
872 {
873 ArtRenderPriv *priv = (ArtRenderPriv *)render;
874 int n_mask_source = priv->n_mask_source++;
876 if (n_mask_source == 0)
877 priv->mask_source = art_new (ArtMaskSource *, 1);
878 /* This predicate is true iff n_mask_source is a power of two */
879 else if (!(n_mask_source & (n_mask_source - 1)))
880 priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *,
881 n_mask_source << 1);
883 priv->mask_source[n_mask_source] = mask_source;
884 }
886 /**
887 * art_render_add_image_source: Add a mask source to the render object.
888 * @render: Render object.
889 * @image_source: Image source to add.
890 *
891 * This routine adds an image source to the render object. In general,
892 * client api's for adding image sources should just take a render
893 * object, then the mask source creation function should call this
894 * function. Clients should never have to call this function
895 * directly, unless of course they're creating custom image sources.
896 **/
897 void
898 art_render_add_image_source (ArtRender *render, ArtImageSource *image_source)
899 {
900 ArtRenderPriv *priv = (ArtRenderPriv *)render;
902 if (priv->image_source != NULL)
903 {
904 art_warn ("art_render_add_image_source: image source already present.\n");
905 return;
906 }
907 priv->image_source = image_source;
908 }
910 /* Solid image source object and methods. Perhaps this should go into a
911 separate file. */
913 typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
915 struct _ArtImageSourceSolid {
916 ArtImageSource super;
917 ArtPixMaxDepth color[ART_MAX_CHAN];
918 art_u32 *rgbtab;
919 art_boolean init;
920 };
922 static void
923 art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render)
924 {
925 ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
927 if (z->rgbtab != NULL)
928 art_free (z->rgbtab);
929 art_free (self);
930 }
932 static void
933 art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render)
934 {
935 ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
936 ArtPixMaxDepth color_max;
937 int r_fg, g_fg, b_fg;
938 int r_bg, g_bg, b_bg;
939 int r, g, b;
940 int dr, dg, db;
941 int i;
942 int tmp;
943 art_u32 *rgbtab;
945 rgbtab = art_new (art_u32, 256);
946 z->rgbtab = rgbtab;
948 color_max = self->color[0];
949 r_fg = ART_PIX_8_FROM_MAX (color_max);
950 color_max = self->color[1];
951 g_fg = ART_PIX_8_FROM_MAX (color_max);
952 color_max = self->color[2];
953 b_fg = ART_PIX_8_FROM_MAX (color_max);
955 color_max = render->clear_color[0];
956 r_bg = ART_PIX_8_FROM_MAX (color_max);
957 color_max = render->clear_color[1];
958 g_bg = ART_PIX_8_FROM_MAX (color_max);
959 color_max = render->clear_color[2];
960 b_bg = ART_PIX_8_FROM_MAX (color_max);
962 r = (r_bg << 16) + 0x8000;
963 g = (g_bg << 16) + 0x8000;
964 b = (b_bg << 16) + 0x8000;
965 tmp = ((r_fg - r_bg) << 16) + 0x80;
966 dr = (tmp + (tmp >> 8)) >> 8;
967 tmp = ((g_fg - g_bg) << 16) + 0x80;
968 dg = (tmp + (tmp >> 8)) >> 8;
969 tmp = ((b_fg - b_bg) << 16) + 0x80;
970 db = (tmp + (tmp >> 8)) >> 8;
972 for (i = 0; i < 256; i++)
973 {
974 rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
975 r += dr;
976 g += dg;
977 b += db;
978 }
979 }
981 static void
982 art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render,
983 art_u8 *dest, int y)
984 {
985 ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
986 ArtRenderMaskRun *run = render->run;
987 int n_run = render->n_run;
988 art_u32 *rgbtab = z->rgbtab;
989 art_u32 rgb;
990 int x0 = render->x0;
991 int x1 = render->x1;
992 int run_x0, run_x1;
993 int i;
994 int ix;
996 if (n_run > 0)
997 {
998 run_x1 = run[0].x;
999 if (run_x1 > x0)
1000 {
1001 rgb = rgbtab[0];
1002 art_rgb_fill_run (dest,
1003 rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1004 run_x1 - x0);
1005 }
1006 for (i = 0; i < n_run - 1; i++)
1007 {
1008 run_x0 = run_x1;
1009 run_x1 = run[i + 1].x;
1010 rgb = rgbtab[(run[i].alpha >> 16) & 0xff];
1011 ix = (run_x0 - x0) * 3;
1012 #define OPTIMIZE_LEN_1
1013 #ifdef OPTIMIZE_LEN_1
1014 if (run_x1 - run_x0 == 1)
1015 {
1016 dest[ix] = rgb >> 16;
1017 dest[ix + 1] = (rgb >> 8) & 0xff;
1018 dest[ix + 2] = rgb & 0xff;
1019 }
1020 else
1021 {
1022 art_rgb_fill_run (dest + ix,
1023 rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1024 run_x1 - run_x0);
1025 }
1026 #else
1027 art_rgb_fill_run (dest + ix,
1028 rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1029 run_x1 - run_x0);
1030 #endif
1031 }
1032 }
1033 else
1034 {
1035 run_x1 = x0;
1036 }
1037 if (run_x1 < x1)
1038 {
1039 rgb = rgbtab[0];
1040 art_rgb_fill_run (dest + (run_x1 - x0) * 3,
1041 rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1042 x1 - run_x1);
1043 }
1044 }
1046 static void
1047 art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render,
1048 art_u8 *dest, int y)
1049 {
1050 ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1051 int width = render->x1 - render->x0;
1052 art_u8 r, g, b;
1053 ArtPixMaxDepth color_max;
1055 /* todo: replace this simple test with real sparseness */
1056 if (z->init)
1057 return;
1058 z->init = ART_TRUE;
1060 color_max = z->color[0];
1061 r = ART_PIX_8_FROM_MAX (color_max);
1062 color_max = z->color[1];
1063 g = ART_PIX_8_FROM_MAX (color_max);
1064 color_max = z->color[2];
1065 b = ART_PIX_8_FROM_MAX (color_max);
1067 art_rgb_fill_run (render->image_buf, r, g, b, width);
1068 }
1070 static void
1071 art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render,
1072 ArtImageSourceFlags *p_flags,
1073 int *p_buf_depth, ArtAlphaType *p_alpha)
1074 {
1075 ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1076 ArtImageSourceFlags flags = 0;
1077 static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render,
1078 art_u8 *dest, int y);
1080 render_cbk = NULL;
1082 if (render->depth == 8 && render->n_chan == 3 &&
1083 render->alpha_type == ART_ALPHA_NONE)
1084 {
1085 if (render->clear)
1086 {
1087 render_cbk = art_render_image_solid_rgb8_opaq;
1088 flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE;
1089 art_render_image_solid_rgb8_opaq_init (z, render);
1090 }
1091 }
1092 if (render_cbk == NULL)
1093 {
1094 if (render->depth == 8)
1095 {
1096 render_cbk = art_render_image_solid_rgb8;
1097 *p_buf_depth = 8;
1098 *p_alpha = ART_ALPHA_NONE; /* todo */
1099 }
1100 }
1101 /* todo: general case */
1102 self->super.render = render_cbk;
1103 *p_flags = flags;
1104 }
1106 /**
1107 * art_render_image_solid: Add a solid color image source.
1108 * @render: The render object.
1109 * @color: Color.
1110 *
1111 * Adds an image source with the solid color given by @color. The
1112 * color need not be retained in memory after this call.
1113 **/
1114 void
1115 art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color)
1116 {
1117 ArtImageSourceSolid *image_source;
1118 int i;
1120 image_source = art_new (ArtImageSourceSolid, 1);
1121 image_source->super.super.render = NULL;
1122 image_source->super.super.done = art_render_image_solid_done;
1123 image_source->super.negotiate = art_render_image_solid_negotiate;
1125 for (i = 0; i < render->n_chan; i++)
1126 image_source->color[i] = color[i];
1128 image_source->rgbtab = NULL;
1129 image_source->init = ART_FALSE;
1131 art_render_add_image_source (render, &image_source->super);
1132 }