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  */
22 
23 #include "config.h"
24 #include "art_render.h"
25 
26 #include "art_rgb.h"
27 
28 typedef struct _ArtRenderPriv ArtRenderPriv;
29 
30 struct _ArtRenderPriv {
31   ArtRender super;
32 
33   ArtImageSource *image_source;
34 
35   int n_mask_source;
36   ArtMaskSource **mask_source;
37 
38   int n_callbacks;
39   ArtRenderCallback **callbacks;
40 };
41 
42 ArtRender *
art_render_new(int x0,int y0,int x1,int y1,art_u8 * pixels,int rowstride,int n_chan,int depth,ArtAlphaType alpha_type,ArtAlphaGamma * alphagamma)43 art_render_new (int x0, int y0, int x1, int y1,
44 		art_u8 *pixels, int rowstride,
45 		int n_chan, int depth, ArtAlphaType alpha_type,
46 		ArtAlphaGamma *alphagamma)
47 {
48   ArtRenderPriv *priv;
49   ArtRender *result;
50 
51   priv = art_new (ArtRenderPriv, 1);
52   result = &priv->super;
53 
54   if (n_chan > ART_MAX_CHAN)
55     {
56       art_warn ("art_render_new: n_chan = %d, exceeds %d max\n",
57 		n_chan, ART_MAX_CHAN);
58       return NULL;
59     }
60   if (depth > ART_MAX_DEPTH)
61     {
62       art_warn ("art_render_new: depth = %d, exceeds %d max\n",
63 		depth, ART_MAX_DEPTH);
64       return NULL;
65     }
66   if (x0 >= x1)
67     {
68       art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1);
69       return NULL;
70     }
71   result->x0 = x0;
72   result->y0 = y0;
73   result->x1 = x1;
74   result->y1 = y1;
75   result->pixels = pixels;
76   result->rowstride = rowstride;
77   result->n_chan = n_chan;
78   result->depth = depth;
79   result->alpha_type = alpha_type;
80 
81   result->clear = ART_FALSE;
82   result->opacity = 0x10000;
83   result->compositing_mode = ART_COMPOSITE_NORMAL;
84   result->alphagamma = alphagamma;
85 
86   result->alpha_buf = NULL;
87   result->image_buf = NULL;
88 
89   result->run = NULL;
90   result->span_x = NULL;
91 
92   result->need_span = ART_FALSE;
93 
94   priv->image_source = NULL;
95 
96   priv->n_mask_source = 0;
97   priv->mask_source = NULL;
98 
99   return result;
100 }
101 
102 /* todo on clear routines: I haven't really figured out what to do
103    with clearing the alpha channel. It _should_ be possible to clear
104    to an arbitrary RGBA color. */
105 
106 /**
107  * art_render_clear: Set clear color.
108  * @clear_color: Color with which to clear dest.
109  *
110  * Sets clear color, equivalent to actually clearing the destination
111  * buffer before rendering. This is the most general form.
112  **/
113 void
art_render_clear(ArtRender * render,const ArtPixMaxDepth * clear_color)114 art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color)
115 {
116   int i;
117   int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
118 
119   render->clear = ART_TRUE;
120   for (i = 0; i < n_ch; i++)
121     render->clear_color[i] = clear_color[i];
122 }
123 
124 /**
125  * art_render_clear_rgb: Set clear color, given in RGB format.
126  * @clear_rgb: Clear color, in 0xRRGGBB format.
127  *
128  * Sets clear color, equivalent to actually clearing the destination
129  * buffer before rendering.
130  **/
131 void
art_render_clear_rgb(ArtRender * render,art_u32 clear_rgb)132 art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb)
133 {
134   if (render->n_chan != 3)
135     art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n",
136 	      render->n_chan);
137   else
138     {
139       int r, g, b;
140 
141       render->clear = ART_TRUE;
142       r = clear_rgb >> 16;
143       g = (clear_rgb >> 8) & 0xff;
144       b = clear_rgb & 0xff;
145       render->clear_color[0] = ART_PIX_MAX_FROM_8(r);
146       render->clear_color[1] = ART_PIX_MAX_FROM_8(g);
147       render->clear_color[2] = ART_PIX_MAX_FROM_8(b);
148     }
149 }
150 
151 static void
art_render_nop_done(ArtRenderCallback * self,ArtRender * render)152 art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
153 {
154 }
155 
156 static void
art_render_clear_render_rgb8(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)157 art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render,
158 			      art_u8 *dest, int y)
159 {
160   int width = render->x1 - render->x0;
161   art_u8 r, g, b;
162   ArtPixMaxDepth color_max;
163 
164   color_max = render->clear_color[0];
165   r = ART_PIX_8_FROM_MAX (color_max);
166   color_max = render->clear_color[1];
167   g = ART_PIX_8_FROM_MAX (color_max);
168   color_max = render->clear_color[2];
169   b = ART_PIX_8_FROM_MAX (color_max);
170 
171   art_rgb_fill_run (dest, r, g, b, width);
172 }
173 
174 static void
art_render_clear_render_8(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)175 art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render,
176 			   art_u8 *dest, int y)
177 {
178   int width = render->x1 - render->x0;
179   int i, j;
180   int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
181   int ix;
182   art_u8 color[ART_MAX_CHAN + 1];
183 
184   for (j = 0; j < n_ch; j++)
185     {
186       ArtPixMaxDepth color_max = render->clear_color[j];
187       color[j] = ART_PIX_8_FROM_MAX (color_max);
188     }
189 
190   ix = 0;
191   for (i = 0; i < width; i++)
192     for (j = 0; j < n_ch; j++)
193       dest[ix++] = color[j];
194 }
195 
196 const ArtRenderCallback art_render_clear_rgb8_obj =
197 {
198   art_render_clear_render_rgb8,
199   art_render_nop_done
200 };
201 
202 const ArtRenderCallback art_render_clear_8_obj =
203 {
204   art_render_clear_render_8,
205   art_render_nop_done
206 };
207 
208 #if ART_MAX_DEPTH >= 16
209 
210 static void
art_render_clear_render_16(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)211 art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render,
212 			    art_u8 *dest, int y)
213 {
214   int width = render->x1 - render->x0;
215   int i, j;
216   int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
217   int ix;
218   art_u16 *dest_16 = (art_u16 *)dest;
219   art_u8 color[ART_MAX_CHAN + 1];
220 
221   for (j = 0; j < n_ch; j++)
222     {
223       int color_16 = render->clear_color[j];
224       color[j] = color_16;
225     }
226 
227   ix = 0;
228   for (i = 0; i < width; i++)
229     for (j = 0; j < n_ch; j++)
230       dest_16[ix++] = color[j];
231 }
232 
233 const ArtRenderCallback art_render_clear_16_obj =
234 {
235   art_render_clear_render_16,
236   art_render_nop_done
237 };
238 
239 #endif /* ART_MAX_DEPTH >= 16 */
240 
241 /* todo: inline */
242 static ArtRenderCallback *
art_render_choose_clear_callback(ArtRender * render)243 art_render_choose_clear_callback (ArtRender *render)
244 {
245   ArtRenderCallback *clear_callback;
246 
247   if (render->depth == 8)
248     {
249       if (render->n_chan == 3 &&
250 	  render->alpha_type == ART_ALPHA_NONE)
251 	clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj;
252       else
253 	clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj;
254     }
255 #if ART_MAX_DEPTH >= 16
256   else if (render->depth == 16)
257     clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj;
258 #endif
259   else
260     {
261       art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n",
262 	       render->depth);
263     }
264   return clear_callback;
265 }
266 
267 #if 0
268 /* todo: get around to writing this */
269 static void
270 art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render,
271 					art_u8 *dest, int y)
272 {
273   int width = render->x1 - render->x0;
274 
275 }
276 #endif
277 
278 /* This is the most general form of the function. It is slow but
279    (hopefully) correct. Actually, I'm still worried about roundoff
280    errors in the premul case - it seems to me that an off-by-one could
281    lead to overflow. */
282 static void
art_render_composite(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)283 art_render_composite (ArtRenderCallback *self, ArtRender *render,
284 					art_u8 *dest, int y)
285 {
286   ArtRenderMaskRun *run = render->run;
287   art_u32 depth = render->depth;
288   int n_run = render->n_run;
289   int x0 = render->x0;
290   int x;
291   int run_x0, run_x1;
292   art_u8 *alpha_buf = render->alpha_buf;
293   art_u8 *image_buf = render->image_buf;
294   int i, j;
295   art_u32 tmp;
296   art_u32 run_alpha;
297   art_u32 alpha;
298   int image_ix;
299   art_u16 src[ART_MAX_CHAN + 1];
300   art_u16 dst[ART_MAX_CHAN + 1];
301   int n_chan = render->n_chan;
302   ArtAlphaType alpha_type = render->alpha_type;
303   int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
304   int dst_pixstride = n_ch * (depth >> 3);
305   int buf_depth = render->buf_depth;
306   ArtAlphaType buf_alpha = render->buf_alpha;
307   int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
308   int buf_pixstride = buf_n_ch * (buf_depth >> 3);
309   art_u8 *bufptr;
310   art_u32 src_alpha;
311   art_u32 src_mul;
312   art_u8 *dstptr;
313   art_u32 dst_alpha;
314   art_u32 dst_mul;
315 
316   image_ix = 0;
317   for (i = 0; i < n_run - 1; i++)
318     {
319       run_x0 = run[i].x;
320       run_x1 = run[i + 1].x;
321       tmp = run[i].alpha;
322       if (tmp < 0x8100)
323 	continue;
324 
325       run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
326       bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
327       dstptr = dest + (run_x0 - x0) * dst_pixstride;
328       for (x = run_x0; x < run_x1; x++)
329 	{
330 	  if (alpha_buf)
331 	    {
332 	      if (depth == 8)
333 		{
334 		  tmp = run_alpha * alpha_buf[x - x0] + 0x80;
335 		  /* range 0x80 .. 0xff0080 */
336 		  alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
337 		}
338 	      else /* (depth == 16) */
339 		{
340 		  tmp = ((art_u16 *)alpha_buf)[x - x0];
341 		  tmp = (run_alpha * tmp + 0x8000) >> 8;
342 		  /* range 0x80 .. 0xffff80 */
343 		  alpha = (tmp + (tmp >> 16)) >> 8;
344 		}
345 	    }
346 	  else
347 	    alpha = run_alpha;
348 	  /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
349 
350 	  /* convert (src pixel * alpha) to premul alpha form,
351 	     store in src as 0..0xffff range */
352 	  if (buf_alpha == ART_ALPHA_NONE)
353 	    {
354 	      src_alpha = alpha;
355 	      src_mul = src_alpha;
356 	    }
357 	  else
358 	    {
359 	      if (buf_depth == 8)
360 		{
361 		  tmp = alpha * bufptr[n_chan] + 0x80;
362 		  /* range 0x80 .. 0xff0080 */
363 		  src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
364 		}
365 	      else /* (depth == 16) */
366 		{
367 		  tmp = ((art_u16 *)bufptr)[n_chan];
368 		  tmp = (alpha * tmp + 0x8000) >> 8;
369 		  /* range 0x80 .. 0xffff80 */
370 		  src_alpha = (tmp + (tmp >> 16)) >> 8;
371 		}
372 	      if (buf_alpha == ART_ALPHA_SEPARATE)
373 		src_mul = src_alpha;
374 	      else /* buf_alpha == (ART_ALPHA_PREMUL) */
375 		src_mul = alpha;
376 	    }
377 	  /* src_alpha is the (alpha of the source pixel * alpha),
378 	     range 0..0x10000 */
379 
380 	  if (buf_depth == 8)
381 	    {
382 	      src_mul *= 0x101;
383 	      for (j = 0; j < n_chan; j++)
384 		src[j] = (bufptr[j] * src_mul + 0x8000) >> 16;
385 	    }
386 	  else if (buf_depth == 16)
387 	    {
388 	      for (j = 0; j < n_chan; j++)
389 		src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16;
390 	    }
391 	  bufptr += buf_pixstride;
392 
393 	  /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range
394              0..0x10000) now contain the source pixel with
395              premultiplied alpha */
396 
397 	  /* convert dst pixel to premul alpha form,
398 	     store in dst as 0..0xffff range */
399 	  if (alpha_type == ART_ALPHA_NONE)
400 	    {
401 	      dst_alpha = 0x10000;
402 	      dst_mul = dst_alpha;
403 	    }
404 	  else
405 	    {
406 	      if (depth == 8)
407 		{
408 		  tmp = dstptr[n_chan];
409 		  /* range 0..0xff */
410 		  dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
411 		}
412 	      else /* (depth == 16) */
413 		{
414 		  tmp = ((art_u16 *)dstptr)[n_chan];
415 		  dst_alpha = (tmp + (tmp >> 15));
416 		}
417 	      if (alpha_type == ART_ALPHA_SEPARATE)
418 		dst_mul = dst_alpha;
419 	      else /* (alpha_type == ART_ALPHA_PREMUL) */
420 		dst_mul = 0x10000;
421 	    }
422 	  /* dst_alpha is the alpha of the dest pixel,
423 	     range 0..0x10000 */
424 
425 	  if (depth == 8)
426 	    {
427 	      dst_mul *= 0x101;
428 	      for (j = 0; j < n_chan; j++)
429 		dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16;
430 	    }
431 	  else if (buf_depth == 16)
432 	    {
433 	      for (j = 0; j < n_chan; j++)
434 		dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16;
435 	    }
436 
437 	  /* do the compositing, dst = (src over dst) */
438 	  for (j = 0; j < n_chan; j++)
439 	    {
440 	      art_u32 srcv, dstv;
441 	      art_u32 tmp;
442 
443 	      srcv = src[j];
444 	      dstv = dst[j];
445 	      tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv;
446 	      tmp -= tmp >> 16;
447 	      dst[j] = tmp;
448 	    }
449 
450 	  if (alpha_type == ART_ALPHA_NONE)
451 	    {
452 	      if (depth == 8)
453 		dst_mul = 0xff;
454 	      else /* (depth == 16) */
455 		dst_mul = 0xffff;
456 	    }
457 	  else
458 	    {
459 	      if (src_alpha >= 0x10000)
460 		dst_alpha = 0x10000;
461 	      else
462 		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
463 	      if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
464 		{
465 		  if (depth == 8)
466 		    dst_mul = 0xff;
467 		  else /* (depth == 16) */
468 		    dst_mul = 0xffff;
469 		}
470 	      else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
471 		{
472 		  if (depth == 8)
473 		    dst_mul = 0xff0000 / dst_alpha;
474 		  else /* (depth == 16) */
475 		    dst_mul = 0xffff0000 / dst_alpha;
476 		}
477 	    }
478 	  if (depth == 8)
479 	    {
480 	      for (j = 0; j < n_chan; j++)
481 		dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16;
482 	      if (alpha_type != ART_ALPHA_NONE)
483 		dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
484 	    }
485 	  else if (depth == 16)
486 	    {
487 	      for (j = 0; j < n_chan; j++)
488 		((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16;
489 	      if (alpha_type != ART_ALPHA_NONE)
490 		((art_u16 *)dstptr)[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16;
491 	    }
492 	  dstptr += dst_pixstride;
493 	}
494     }
495 }
496 
497 const ArtRenderCallback art_render_composite_obj =
498 {
499   art_render_composite,
500   art_render_nop_done
501 };
502 
503 static void
art_render_composite_8(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)504 art_render_composite_8 (ArtRenderCallback *self, ArtRender *render,
505 			art_u8 *dest, int y)
506 {
507   ArtRenderMaskRun *run = render->run;
508   int n_run = render->n_run;
509   int x0 = render->x0;
510   int x;
511   int run_x0, run_x1;
512   art_u8 *alpha_buf = render->alpha_buf;
513   art_u8 *image_buf = render->image_buf;
514   int i, j;
515   art_u32 tmp;
516   art_u32 run_alpha;
517   art_u32 alpha;
518   int image_ix;
519   int n_chan = render->n_chan;
520   ArtAlphaType alpha_type = render->alpha_type;
521   int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
522   int dst_pixstride = n_ch;
523   ArtAlphaType buf_alpha = render->buf_alpha;
524   int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
525   int buf_pixstride = buf_n_ch;
526   art_u8 *bufptr;
527   art_u32 src_alpha;
528   art_u32 src_mul;
529   art_u8 *dstptr;
530   art_u32 dst_alpha;
531   art_u32 dst_mul, dst_save_mul;
532 
533   image_ix = 0;
534   for (i = 0; i < n_run - 1; i++)
535     {
536       run_x0 = run[i].x;
537       run_x1 = run[i + 1].x;
538       tmp = run[i].alpha;
539       if (tmp < 0x10000)
540 	continue;
541 
542       run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
543       bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
544       dstptr = dest + (run_x0 - x0) * dst_pixstride;
545       for (x = run_x0; x < run_x1; x++)
546 	{
547 	  if (alpha_buf)
548 	    {
549 	      tmp = run_alpha * alpha_buf[x - x0] + 0x80;
550 	      /* range 0x80 .. 0xff0080 */
551 	      alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
552 	    }
553 	  else
554 	    alpha = run_alpha;
555 	  /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
556 
557 	  /* convert (src pixel * alpha) to premul alpha form,
558 	     store in src as 0..0xffff range */
559 	  if (buf_alpha == ART_ALPHA_NONE)
560 	    {
561 	      src_alpha = alpha;
562 	      src_mul = src_alpha;
563 	    }
564 	  else
565 	    {
566 	      tmp = alpha * bufptr[n_chan] + 0x80;
567 	      /* range 0x80 .. 0xff0080 */
568 	      src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
569 
570 	      if (buf_alpha == ART_ALPHA_SEPARATE)
571 		src_mul = src_alpha;
572 	      else /* buf_alpha == (ART_ALPHA_PREMUL) */
573 		src_mul = alpha;
574 	    }
575 	  /* src_alpha is the (alpha of the source pixel * alpha),
576 	     range 0..0x10000 */
577 
578 	  src_mul *= 0x101;
579 
580 	  if (alpha_type == ART_ALPHA_NONE)
581 	    {
582 	      dst_alpha = 0x10000;
583 	      dst_mul = dst_alpha;
584 	    }
585 	  else
586 	    {
587 	      tmp = dstptr[n_chan];
588 	      /* range 0..0xff */
589 	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
590 	      if (alpha_type == ART_ALPHA_SEPARATE)
591 		dst_mul = dst_alpha;
592 	      else /* (alpha_type == ART_ALPHA_PREMUL) */
593 		dst_mul = 0x10000;
594 	    }
595 	  /* dst_alpha is the alpha of the dest pixel,
596 	     range 0..0x10000 */
597 
598 	  dst_mul *= 0x101;
599 
600 	  if (alpha_type == ART_ALPHA_NONE)
601 	    {
602 	      dst_save_mul = 0xff;
603 	    }
604 	  else
605 	    {
606 	      if (src_alpha >= 0x10000)
607 		dst_alpha = 0x10000;
608 	      else
609 		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
610 	      if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
611 		{
612 		  dst_save_mul = 0xff;
613 		}
614 	      else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
615 		{
616 		  dst_save_mul = 0xff0000 / dst_alpha;
617 		}
618 	    }
619 	  for (j = 0; j < n_chan; j++)
620 	    {
621 	      art_u32 src, dst;
622 	      art_u32 tmp;
623 
624 	      src = (bufptr[j] * src_mul + 0x8000) >> 16;
625 	      dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
626 	      tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
627 	      tmp -= tmp >> 16;
628 	      dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
629 	    }
630 	  if (alpha_type != ART_ALPHA_NONE)
631 	    dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
632 
633 	  bufptr += buf_pixstride;
634 	  dstptr += dst_pixstride;
635 	}
636     }
637 }
638 
639 const ArtRenderCallback art_render_composite_8_obj =
640 {
641   art_render_composite_8,
642   art_render_nop_done
643 };
644 
645 
646 /* Assumes:
647  * alpha_buf is NULL
648  * buf_alpha = ART_ALPHA_NONE  (source)
649  * alpha_type = ART_ALPHA_SEPARATE (dest)
650  * n_chan = 3;
651  */
652 static void
art_render_composite_8_opt1(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)653 art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render,
654 			     art_u8 *dest, int y)
655 {
656   ArtRenderMaskRun *run = render->run;
657   int n_run = render->n_run;
658   int x0 = render->x0;
659   int x;
660   int run_x0, run_x1;
661   art_u8 *image_buf = render->image_buf;
662   int i, j;
663   art_u32 tmp;
664   art_u32 run_alpha;
665   int image_ix;
666   art_u8 *bufptr;
667   art_u32 src_mul;
668   art_u8 *dstptr;
669   art_u32 dst_alpha;
670   art_u32 dst_mul, dst_save_mul;
671 
672   image_ix = 0;
673   for (i = 0; i < n_run - 1; i++)
674     {
675       run_x0 = run[i].x;
676       run_x1 = run[i + 1].x;
677       tmp = run[i].alpha;
678       if (tmp < 0x10000)
679 	continue;
680 
681       run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
682       bufptr = image_buf + (run_x0 - x0) * 3;
683       dstptr = dest + (run_x0 - x0) * 4;
684       if (run_alpha == 0x10000)
685 	{
686 	  for (x = run_x0; x < run_x1; x++)
687 	    {
688 	      *dstptr++ = *bufptr++;
689 	      *dstptr++ = *bufptr++;
690 	      *dstptr++ = *bufptr++;
691 	      *dstptr++ = 0xff;
692 	    }
693 	}
694       else
695 	{
696 	  for (x = run_x0; x < run_x1; x++)
697 	    {
698 	      src_mul = run_alpha * 0x101;
699 
700 	      tmp = dstptr[3];
701 	      /* range 0..0xff */
702 	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
703 	      dst_mul = dst_alpha;
704 	      /* dst_alpha is the alpha of the dest pixel,
705 		 range 0..0x10000 */
706 
707 	      dst_mul *= 0x101;
708 
709 	      dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8;
710 	      if (dst_alpha == 0)
711 		  dst_save_mul = 0xff;
712 	      else /* (dst_alpha != 0) */
713 		  dst_save_mul = 0xff0000 / dst_alpha;
714 
715 	      for (j = 0; j < 3; j++)
716 		{
717 		  art_u32 src, dst;
718 		  art_u32 tmp;
719 
720 		  src = (bufptr[j] * src_mul + 0x8000) >> 16;
721 		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
722 		  tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src;
723 		  tmp -= tmp >> 16;
724 		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
725 		}
726 	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
727 
728 	      bufptr += 3;
729 	      dstptr += 4;
730 	    }
731 	}
732     }
733 }
734 
735 
736 const ArtRenderCallback art_render_composite_8_opt1_obj =
737 {
738   art_render_composite_8_opt1,
739   art_render_nop_done
740 };
741 
742 /* Assumes:
743  * alpha_buf is NULL
744  * buf_alpha = ART_ALPHA_PREMUL  (source)
745  * alpha_type = ART_ALPHA_SEPARATE (dest)
746  * n_chan = 3;
747  */
748 static void
art_render_composite_8_opt2(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)749 art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render,
750 			     art_u8 *dest, int y)
751 {
752   ArtRenderMaskRun *run = render->run;
753   int n_run = render->n_run;
754   int x0 = render->x0;
755   int x;
756   int run_x0, run_x1;
757   art_u8 *image_buf = render->image_buf;
758   int i, j;
759   art_u32 tmp;
760   art_u32 run_alpha;
761   int image_ix;
762   art_u8 *bufptr;
763   art_u32 src_alpha;
764   art_u32 src_mul;
765   art_u8 *dstptr;
766   art_u32 dst_alpha;
767   art_u32 dst_mul, dst_save_mul;
768 
769   image_ix = 0;
770   for (i = 0; i < n_run - 1; i++)
771     {
772       run_x0 = run[i].x;
773       run_x1 = run[i + 1].x;
774       tmp = run[i].alpha;
775       if (tmp < 0x10000)
776 	continue;
777 
778       run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
779       bufptr = image_buf + (run_x0 - x0) * 4;
780       dstptr = dest + (run_x0 - x0) * 4;
781       if (run_alpha == 0x10000)
782 	{
783 	  for (x = run_x0; x < run_x1; x++)
784 	    {
785 	      src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7);
786 	      /* src_alpha is the (alpha of the source pixel),
787 		 range 0..0x10000 */
788 
789 	      dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7);
790 	      /* dst_alpha is the alpha of the dest pixel,
791 		 range 0..0x10000 */
792 
793 	      dst_mul = dst_alpha*0x101;
794 
795 	      if (src_alpha >= 0x10000)
796 		dst_alpha = 0x10000;
797 	      else
798 		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
799 
800 	      if (dst_alpha == 0)
801 		  dst_save_mul = 0xff;
802 	      else /* dst_alpha != 0) */
803 		  dst_save_mul = 0xff0000 / dst_alpha;
804 
805 	      for (j = 0; j < 3; j++)
806 		{
807 		  art_u32 src, dst;
808 		  art_u32 tmp;
809 
810 		  src = (bufptr[j] << 8) |  bufptr[j];
811 		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
812 		  tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
813 		  tmp -= tmp >> 16;
814 		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
815 		}
816 	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
817 
818 	      bufptr += 4;
819 	      dstptr += 4;
820 	    }
821 	}
822       else
823 	{
824 	  for (x = run_x0; x < run_x1; x++)
825 	    {
826 	      tmp = run_alpha * bufptr[3] + 0x80;
827 	      /* range 0x80 .. 0xff0080 */
828 	      src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
829 	      /* src_alpha is the (alpha of the source pixel * alpha),
830 		 range 0..0x10000 */
831 
832 	      src_mul = run_alpha * 0x101;
833 
834 	      tmp = dstptr[3];
835 	      /* range 0..0xff */
836 	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
837 	      dst_mul = dst_alpha;
838 	      /* dst_alpha is the alpha of the dest pixel,
839 		 range 0..0x10000 */
840 
841 	      dst_mul *= 0x101;
842 
843 	      if (src_alpha >= 0x10000)
844 		dst_alpha = 0x10000;
845 	      else
846 		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
847 
848 	      if (dst_alpha == 0)
849 		{
850 		  dst_save_mul = 0xff;
851 		}
852 	      else /* dst_alpha != 0) */
853 		{
854 		  dst_save_mul = 0xff0000 / dst_alpha;
855 		}
856 
857 	      for (j = 0; j < 3; j++)
858 		{
859 		  art_u32 src, dst;
860 		  art_u32 tmp;
861 
862 		  src = (bufptr[j] * src_mul + 0x8000) >> 16;
863 		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
864 		  tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
865 		  tmp -= tmp >> 16;
866 		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
867 		}
868 	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
869 
870 	      bufptr += 4;
871 	      dstptr += 4;
872 	    }
873 	}
874     }
875 }
876 
877 const ArtRenderCallback art_render_composite_8_opt2_obj =
878 {
879   art_render_composite_8_opt2,
880   art_render_nop_done
881 };
882 
883 
884 /* todo: inline */
885 static ArtRenderCallback *
art_render_choose_compositing_callback(ArtRender * render)886 art_render_choose_compositing_callback (ArtRender *render)
887 {
888   if (render->depth == 8 && render->buf_depth == 8)
889     {
890       if (render->n_chan == 3 &&
891 	  render->alpha_buf == NULL &&
892 	  render->alpha_type == ART_ALPHA_SEPARATE)
893 	{
894 	  if (render->buf_alpha == ART_ALPHA_NONE)
895 	    return (ArtRenderCallback *)&art_render_composite_8_opt1_obj;
896 	  else if (render->buf_alpha == ART_ALPHA_PREMUL)
897 	    return (ArtRenderCallback *)&art_render_composite_8_opt2_obj;
898 	}
899 
900       return (ArtRenderCallback *)&art_render_composite_8_obj;
901     }
902   return (ArtRenderCallback *)&art_render_composite_obj;
903 }
904 
905 /**
906  * art_render_invoke_callbacks: Invoke the callbacks in the render object.
907  * @render: The render object.
908  * @y: The current Y coordinate value.
909  *
910  * Invokes the callbacks of the render object in the appropriate
911  * order.  Drivers should call this routine once per scanline.
912  *
913  * todo: should management of dest devolve to this routine? very
914  * plausibly yes.
915  **/
916 void
art_render_invoke_callbacks(ArtRender * render,art_u8 * dest,int y)917 art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y)
918 {
919   ArtRenderPriv *priv = (ArtRenderPriv *)render;
920   int i;
921 
922   for (i = 0; i < priv->n_callbacks; i++)
923     {
924       ArtRenderCallback *callback;
925 
926       callback = priv->callbacks[i];
927       callback->render (callback, render, dest, y);
928     }
929 }
930 
931 /**
932  * art_render_invoke: Perform the requested rendering task.
933  * @render: The render object.
934  *
935  * Invokes the renderer and all sources associated with it, to perform
936  * the requested rendering task.
937  **/
938 void
art_render_invoke(ArtRender * render)939 art_render_invoke (ArtRender *render)
940 {
941   ArtRenderPriv *priv = (ArtRenderPriv *)render;
942   int width;
943   int best_driver, best_score;
944   int i;
945   int n_callbacks, n_callbacks_max;
946   ArtImageSource *image_source;
947   ArtImageSourceFlags image_flags;
948   int buf_depth;
949   ArtAlphaType buf_alpha;
950   art_boolean first = ART_TRUE;
951 
952   if (render == NULL)
953     {
954       art_warn ("art_render_invoke: called with render == NULL\n");
955       return;
956     }
957   if (priv->image_source == NULL)
958     {
959       art_warn ("art_render_invoke: no image source given\n");
960       return;
961     }
962 
963   width = render->x1 - render->x0;
964 
965   render->run = art_new (ArtRenderMaskRun, width + 1);
966 
967   /* Elect a mask source as driver. */
968   best_driver = -1;
969   best_score = 0;
970   for (i = 0; i < priv->n_mask_source; i++)
971     {
972       int score;
973       ArtMaskSource *mask_source;
974 
975       mask_source = priv->mask_source[i];
976       score = mask_source->can_drive (mask_source, render);
977       if (score > best_score)
978 	{
979 	  best_score = score;
980 	  best_driver = i;
981 	}
982     }
983 
984   /* Allocate alpha buffer if needed. */
985   if (priv->n_mask_source > 1 ||
986       (priv->n_mask_source == 1 && best_driver < 0))
987     {
988       render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3);
989     }
990 
991   /* Negotiate image rendering and compositing. */
992   image_source = priv->image_source;
993   image_source->negotiate (image_source, render, &image_flags, &buf_depth,
994 			   &buf_alpha);
995 
996   /* Build callback list. */
997   n_callbacks_max = priv->n_mask_source + 3;
998   priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max);
999   n_callbacks = 0;
1000   for (i = 0; i < priv->n_mask_source; i++)
1001     if (i != best_driver)
1002       {
1003 	ArtMaskSource *mask_source = priv->mask_source[i];
1004 
1005 	mask_source->prepare (mask_source, render, first);
1006 	first = ART_FALSE;
1007 	priv->callbacks[n_callbacks++] = &mask_source->super;
1008       }
1009 
1010   if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR))
1011     priv->callbacks[n_callbacks++] =
1012       art_render_choose_clear_callback (render);
1013 
1014   priv->callbacks[n_callbacks++] = &image_source->super;
1015 
1016   /* Allocate image buffer and add compositing callback if needed. */
1017   if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE))
1018     {
1019       int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) *
1020 		     buf_depth) >> 3;
1021       render->buf_depth = buf_depth;
1022       render->buf_alpha = buf_alpha;
1023       render->image_buf = art_new (art_u8, width * bytespp);
1024       priv->callbacks[n_callbacks++] =
1025 	art_render_choose_compositing_callback (render);
1026     }
1027 
1028   priv->n_callbacks = n_callbacks;
1029 
1030   if (render->need_span)
1031     render->span_x = art_new (int, width + 1);
1032 
1033   /* Invoke the driver */
1034   if (best_driver >= 0)
1035     {
1036       ArtMaskSource *driver;
1037 
1038       driver = priv->mask_source[best_driver];
1039       driver->invoke_driver (driver, render);
1040     }
1041   else
1042     {
1043       art_u8 *dest_ptr = render->pixels;
1044       int y;
1045 
1046       /* Dummy driver */
1047       render->n_run = 2;
1048       render->run[0].x = render->x0;
1049       render->run[0].alpha = 0x8000 + 0xff * render->opacity;
1050       render->run[1].x = render->x1;
1051       render->run[1].alpha = 0x8000;
1052       if (render->need_span)
1053 	{
1054 	  render->n_span = 2;
1055 	  render->span_x[0] = render->x0;
1056 	  render->span_x[1] = render->x1;
1057 	}
1058       for (y = render->y0; y < render->y1; y++)
1059 	{
1060 	  art_render_invoke_callbacks (render, dest_ptr, y);
1061 	  dest_ptr += render->rowstride;
1062 	}
1063     }
1064 
1065   if (priv->mask_source != NULL)
1066     art_free (priv->mask_source);
1067 
1068   /* clean up callbacks */
1069   for (i = 0; i < priv->n_callbacks; i++)
1070     {
1071       ArtRenderCallback *callback;
1072 
1073       callback = priv->callbacks[i];
1074       callback->done (callback, render);
1075     }
1076 
1077   /* Tear down object */
1078   if (render->alpha_buf != NULL)
1079     art_free (render->alpha_buf);
1080   if (render->image_buf != NULL)
1081     art_free (render->image_buf);
1082   art_free (render->run);
1083   if (render->span_x != NULL)
1084     art_free (render->span_x);
1085   art_free (priv->callbacks);
1086   art_free (render);
1087 }
1088 
1089 /**
1090  * art_render_mask_solid: Add a solid translucent mask.
1091  * @render: The render object.
1092  * @opacity: Opacity in [0..0x10000] form.
1093  *
1094  * Adds a translucent mask to the rendering object.
1095  **/
1096 void
art_render_mask_solid(ArtRender * render,int opacity)1097 art_render_mask_solid (ArtRender *render, int opacity)
1098 {
1099   art_u32 old_opacity = render->opacity;
1100   art_u32 new_opacity_tmp;
1101 
1102   if (opacity == 0x10000)
1103     /* avoid potential overflow */
1104     return;
1105   new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000;
1106   render->opacity = new_opacity_tmp >> 16;
1107 }
1108 
1109 /**
1110  * art_render_add_mask_source: Add a mask source to the render object.
1111  * @render: Render object.
1112  * @mask_source: Mask source to add.
1113  *
1114  * This routine adds a mask source to the render object. In general,
1115  * client api's for adding mask sources should just take a render object,
1116  * then the mask source creation function should call this function.
1117  * Clients should never have to call this function directly, unless of
1118  * course they're creating custom mask sources.
1119  **/
1120 void
art_render_add_mask_source(ArtRender * render,ArtMaskSource * mask_source)1121 art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source)
1122 {
1123   ArtRenderPriv *priv = (ArtRenderPriv *)render;
1124   int n_mask_source = priv->n_mask_source++;
1125 
1126   if (n_mask_source == 0)
1127     priv->mask_source = art_new (ArtMaskSource *, 1);
1128   /* This predicate is true iff n_mask_source is a power of two */
1129   else if (!(n_mask_source & (n_mask_source - 1)))
1130     priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *,
1131 				   n_mask_source << 1);
1132 
1133   priv->mask_source[n_mask_source] = mask_source;
1134 }
1135 
1136 /**
1137  * art_render_add_image_source: Add a mask source to the render object.
1138  * @render: Render object.
1139  * @image_source: Image source to add.
1140  *
1141  * This routine adds an image source to the render object. In general,
1142  * client api's for adding image sources should just take a render
1143  * object, then the mask source creation function should call this
1144  * function.  Clients should never have to call this function
1145  * directly, unless of course they're creating custom image sources.
1146  **/
1147 void
art_render_add_image_source(ArtRender * render,ArtImageSource * image_source)1148 art_render_add_image_source (ArtRender *render, ArtImageSource *image_source)
1149 {
1150   ArtRenderPriv *priv = (ArtRenderPriv *)render;
1151 
1152   if (priv->image_source != NULL)
1153     {
1154       art_warn ("art_render_add_image_source: image source already present.\n");
1155       return;
1156     }
1157   priv->image_source = image_source;
1158 }
1159 
1160 /* Solid image source object and methods. Perhaps this should go into a
1161    separate file. */
1162 
1163 typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
1164 
1165 struct _ArtImageSourceSolid {
1166   ArtImageSource super;
1167   ArtPixMaxDepth color[ART_MAX_CHAN];
1168   art_u32 *rgbtab;
1169   art_boolean init;
1170 };
1171 
1172 static void
art_render_image_solid_done(ArtRenderCallback * self,ArtRender * render)1173 art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render)
1174 {
1175   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1176 
1177   if (z->rgbtab != NULL)
1178     art_free (z->rgbtab);
1179   art_free (self);
1180 }
1181 
1182 static void
art_render_image_solid_rgb8_opaq_init(ArtImageSourceSolid * self,ArtRender * render)1183 art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render)
1184 {
1185   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1186   ArtPixMaxDepth color_max;
1187   int r_fg, g_fg, b_fg;
1188   int r_bg, g_bg, b_bg;
1189   int r, g, b;
1190   int dr, dg, db;
1191   int i;
1192   int tmp;
1193   art_u32 *rgbtab;
1194 
1195   rgbtab = art_new (art_u32, 256);
1196   z->rgbtab = rgbtab;
1197 
1198   color_max = self->color[0];
1199   r_fg = ART_PIX_8_FROM_MAX (color_max);
1200   color_max = self->color[1];
1201   g_fg = ART_PIX_8_FROM_MAX (color_max);
1202   color_max = self->color[2];
1203   b_fg = ART_PIX_8_FROM_MAX (color_max);
1204 
1205   color_max = render->clear_color[0];
1206   r_bg = ART_PIX_8_FROM_MAX (color_max);
1207   color_max = render->clear_color[1];
1208   g_bg = ART_PIX_8_FROM_MAX (color_max);
1209   color_max = render->clear_color[2];
1210   b_bg = ART_PIX_8_FROM_MAX (color_max);
1211 
1212   r = (r_bg << 16) + 0x8000;
1213   g = (g_bg << 16) + 0x8000;
1214   b = (b_bg << 16) + 0x8000;
1215   tmp = ((r_fg - r_bg) << 16) + 0x80;
1216   dr = (tmp + (tmp >> 8)) >> 8;
1217   tmp = ((g_fg - g_bg) << 16) + 0x80;
1218   dg = (tmp + (tmp >> 8)) >> 8;
1219   tmp = ((b_fg - b_bg) << 16) + 0x80;
1220   db = (tmp + (tmp >> 8)) >> 8;
1221 
1222   for (i = 0; i < 256; i++)
1223     {
1224       rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
1225       r += dr;
1226       g += dg;
1227       b += db;
1228     }
1229 }
1230 
1231 static void
art_render_image_solid_rgb8_opaq(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)1232 art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render,
1233 				  art_u8 *dest, int y)
1234 {
1235   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1236   ArtRenderMaskRun *run = render->run;
1237   int n_run = render->n_run;
1238   art_u32 *rgbtab = z->rgbtab;
1239   art_u32 rgb;
1240   int x0 = render->x0;
1241   int x1 = render->x1;
1242   int run_x0, run_x1;
1243   int i;
1244   int ix;
1245 
1246   if (n_run > 0)
1247     {
1248       run_x1 = run[0].x;
1249       if (run_x1 > x0)
1250 	{
1251 	  rgb = rgbtab[0];
1252 	  art_rgb_fill_run (dest,
1253 			    (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff),
1254 			    run_x1 - x0);
1255 	}
1256       for (i = 0; i < n_run - 1; i++)
1257 	{
1258 	  run_x0 = run_x1;
1259 	  run_x1 = run[i + 1].x;
1260 	  rgb = rgbtab[(run[i].alpha >> 16) & 0xff];
1261 	  ix = (run_x0 - x0) * 3;
1262 #define OPTIMIZE_LEN_1
1263 #ifdef OPTIMIZE_LEN_1
1264 	  if (run_x1 - run_x0 == 1)
1265 	    {
1266 	      dest[ix] = rgb >> 16;
1267 	      dest[ix + 1] = (rgb >> 8) & 0xff;
1268 	      dest[ix + 2] = rgb & 0xff;
1269 	    }
1270 	  else
1271 	    {
1272 	      art_rgb_fill_run (dest + ix,
1273 				(art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff),
1274 				run_x1 - run_x0);
1275 	    }
1276 #else
1277 	  art_rgb_fill_run (dest + ix,
1278 			    (art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff),
1279 			    run_x1 - run_x0);
1280 #endif
1281 	}
1282     }
1283   else
1284     {
1285       run_x1 = x0;
1286     }
1287   if (run_x1 < x1)
1288     {
1289       rgb = rgbtab[0];
1290       art_rgb_fill_run (dest + (run_x1 - x0) * 3,
1291 			(art_u8)(rgb >> 16), (art_u8)((rgb >> 8) & 0xff), (art_u8)(rgb & 0xff),
1292 			x1 - run_x1);
1293     }
1294 }
1295 
1296 static void
art_render_image_solid_rgb8(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)1297 art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render,
1298 			     art_u8 *dest, int y)
1299 {
1300   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1301   int width = render->x1 - render->x0;
1302   art_u8 r, g, b;
1303   ArtPixMaxDepth color_max;
1304 
1305   /* todo: replace this simple test with real sparseness */
1306   if (z->init)
1307     return;
1308   z->init = ART_TRUE;
1309 
1310   color_max = z->color[0];
1311   r = ART_PIX_8_FROM_MAX (color_max);
1312   color_max = z->color[1];
1313   g = ART_PIX_8_FROM_MAX (color_max);
1314   color_max = z->color[2];
1315   b = ART_PIX_8_FROM_MAX (color_max);
1316 
1317   art_rgb_fill_run (render->image_buf, r, g, b, width);
1318 }
1319 
1320 static void
art_render_image_solid_negotiate(ArtImageSource * self,ArtRender * render,ArtImageSourceFlags * p_flags,int * p_buf_depth,ArtAlphaType * p_alpha)1321 art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render,
1322 				  ArtImageSourceFlags *p_flags,
1323 				  int *p_buf_depth, ArtAlphaType *p_alpha)
1324 {
1325   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1326   ArtImageSourceFlags flags = 0;
1327   static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render,
1328 			     art_u8 *dest, int y);
1329 
1330   render_cbk = NULL;
1331 
1332   if (render->depth == 8 && render->n_chan == 3 &&
1333       render->alpha_type == ART_ALPHA_NONE)
1334     {
1335       if (render->clear)
1336 	{
1337 	  render_cbk = art_render_image_solid_rgb8_opaq;
1338 	  flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE;
1339 	  art_render_image_solid_rgb8_opaq_init (z, render);
1340 	}
1341     }
1342   if (render_cbk == NULL)
1343     {
1344       if (render->depth == 8)
1345 	{
1346 	  render_cbk = art_render_image_solid_rgb8;
1347 	  *p_buf_depth = 8;
1348 	  *p_alpha = ART_ALPHA_NONE; /* todo */
1349 	}
1350     }
1351   /* todo: general case */
1352   self->super.render = render_cbk;
1353   *p_flags = flags;
1354 }
1355 
1356 /**
1357  * art_render_image_solid: Add a solid color image source.
1358  * @render: The render object.
1359  * @color: Color.
1360  *
1361  * Adds an image source with the solid color given by @color. The
1362  * color need not be retained in memory after this call.
1363  **/
1364 void
art_render_image_solid(ArtRender * render,ArtPixMaxDepth * color)1365 art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color)
1366 {
1367   ArtImageSourceSolid *image_source;
1368   int i;
1369 
1370   image_source = art_new (ArtImageSourceSolid, 1);
1371   image_source->super.super.render = NULL;
1372   image_source->super.super.done = art_render_image_solid_done;
1373   image_source->super.negotiate = art_render_image_solid_negotiate;
1374 
1375   for (i = 0; i < render->n_chan; i++)
1376     image_source->color[i] = color[i];
1377 
1378   image_source->rgbtab = NULL;
1379   image_source->init = ART_FALSE;
1380 
1381   art_render_add_image_source (render, &image_source->super);
1382 }
1383