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 
620 	  for (j = 0; j < n_chan; j++)
621 	    {
622 	      art_u32 src, dst;
623 	      art_u32 tmp;
624 
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;
633 
634 	  bufptr += buf_pixstride;
635 	  dstptr += dst_pixstride;
636 	}
637     }
638 }
639 
640 const ArtRenderCallback art_render_composite_8_obj =
641 {
642   art_render_composite_8,
643   art_render_nop_done
644 };
645 
646 
647 /* Assumes:
648  * alpha_buf is NULL
649  * buf_alpha = ART_ALPHA_NONE  (source)
650  * alpha_type = ART_ALPHA_SEPARATE (dest)
651  * n_chan = 3;
652  */
653 static void
art_render_composite_8_opt1(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)654 art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render,
655 			     art_u8 *dest, int y)
656 {
657   ArtRenderMaskRun *run = render->run;
658   int n_run = render->n_run;
659   int x0 = render->x0;
660   int x;
661   int run_x0, run_x1;
662   art_u8 *image_buf = render->image_buf;
663   int i, j;
664   art_u32 tmp;
665   art_u32 run_alpha;
666   int image_ix;
667   art_u8 *bufptr;
668   art_u32 src_mul;
669   art_u8 *dstptr;
670   art_u32 dst_alpha;
671   art_u32 dst_mul, dst_save_mul;
672 
673   image_ix = 0;
674   for (i = 0; i < n_run - 1; i++)
675     {
676       run_x0 = run[i].x;
677       run_x1 = run[i + 1].x;
678       tmp = run[i].alpha;
679       if (tmp < 0x10000)
680 	continue;
681 
682       run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
683       bufptr = image_buf + (run_x0 - x0) * 3;
684       dstptr = dest + (run_x0 - x0) * 4;
685       if (run_alpha == 0x10000)
686 	{
687 	  for (x = run_x0; x < run_x1; x++)
688 	    {
689 	      *dstptr++ = *bufptr++;
690 	      *dstptr++ = *bufptr++;
691 	      *dstptr++ = *bufptr++;
692 	      *dstptr++ = 0xff;
693 	    }
694 	}
695       else
696 	{
697 	  for (x = run_x0; x < run_x1; x++)
698 	    {
699 	      src_mul = run_alpha * 0x101;
700 
701 	      tmp = dstptr[3];
702 	      /* range 0..0xff */
703 	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
704 	      dst_mul = dst_alpha;
705 	      /* dst_alpha is the alpha of the dest pixel,
706 		 range 0..0x10000 */
707 
708 	      dst_mul *= 0x101;
709 
710 	      dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8;
711 	      if (dst_alpha == 0)
712 		  dst_save_mul = 0xff;
713 	      else /* (dst_alpha != 0) */
714 		  dst_save_mul = 0xff0000 / dst_alpha;
715 
716 	      for (j = 0; j < 3; j++)
717 		{
718 		  art_u32 src, dst;
719 		  art_u32 tmp;
720 
721 		  src = (bufptr[j] * src_mul + 0x8000) >> 16;
722 		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
723 		  tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src;
724 		  tmp -= tmp >> 16;
725 		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
726 		}
727 	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
728 
729 	      bufptr += 3;
730 	      dstptr += 4;
731 	    }
732 	}
733     }
734 }
735 
736 
737 const ArtRenderCallback art_render_composite_8_opt1_obj =
738 {
739   art_render_composite_8_opt1,
740   art_render_nop_done
741 };
742 
743 /* Assumes:
744  * alpha_buf is NULL
745  * buf_alpha = ART_ALPHA_PREMUL  (source)
746  * alpha_type = ART_ALPHA_SEPARATE (dest)
747  * n_chan = 3;
748  */
749 static void
art_render_composite_8_opt2(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)750 art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render,
751 			     art_u8 *dest, int y)
752 {
753   ArtRenderMaskRun *run = render->run;
754   int n_run = render->n_run;
755   int x0 = render->x0;
756   int x;
757   int run_x0, run_x1;
758   art_u8 *image_buf = render->image_buf;
759   int i, j;
760   art_u32 tmp;
761   art_u32 run_alpha;
762   int image_ix;
763   art_u8 *bufptr;
764   art_u32 src_alpha;
765   art_u32 src_mul;
766   art_u8 *dstptr;
767   art_u32 dst_alpha;
768   art_u32 dst_mul, dst_save_mul;
769 
770   image_ix = 0;
771   for (i = 0; i < n_run - 1; i++)
772     {
773       run_x0 = run[i].x;
774       run_x1 = run[i + 1].x;
775       tmp = run[i].alpha;
776       if (tmp < 0x10000)
777 	continue;
778 
779       run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
780       bufptr = image_buf + (run_x0 - x0) * 4;
781       dstptr = dest + (run_x0 - x0) * 4;
782       if (run_alpha == 0x10000)
783 	{
784 	  for (x = run_x0; x < run_x1; x++)
785 	    {
786 	      src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7);
787 	      /* src_alpha is the (alpha of the source pixel),
788 		 range 0..0x10000 */
789 
790 	      dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7);
791 	      /* dst_alpha is the alpha of the dest pixel,
792 		 range 0..0x10000 */
793 
794 	      dst_mul = dst_alpha*0x101;
795 
796 	      if (src_alpha >= 0x10000)
797 		dst_alpha = 0x10000;
798 	      else
799 		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
800 
801 	      if (dst_alpha == 0)
802 		  dst_save_mul = 0xff;
803 	      else /* dst_alpha != 0) */
804 		  dst_save_mul = 0xff0000 / dst_alpha;
805 
806 	      for (j = 0; j < 3; j++)
807 		{
808 		  art_u32 src, dst;
809 		  art_u32 tmp;
810 
811 		  src = (bufptr[j] << 8) |  bufptr[j];
812 		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
813 		  tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
814 		  tmp -= tmp >> 16;
815 		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
816 		}
817 	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
818 
819 	      bufptr += 4;
820 	      dstptr += 4;
821 	    }
822 	}
823       else
824 	{
825 	  for (x = run_x0; x < run_x1; x++)
826 	    {
827 	      tmp = run_alpha * bufptr[3] + 0x80;
828 	      /* range 0x80 .. 0xff0080 */
829 	      src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
830 	      /* src_alpha is the (alpha of the source pixel * alpha),
831 		 range 0..0x10000 */
832 
833 	      src_mul = run_alpha * 0x101;
834 
835 	      tmp = dstptr[3];
836 	      /* range 0..0xff */
837 	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
838 	      dst_mul = dst_alpha;
839 	      /* dst_alpha is the alpha of the dest pixel,
840 		 range 0..0x10000 */
841 
842 	      dst_mul *= 0x101;
843 
844 	      if (src_alpha >= 0x10000)
845 		dst_alpha = 0x10000;
846 	      else
847 		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
848 
849 	      if (dst_alpha == 0)
850 		{
851 		  dst_save_mul = 0xff;
852 		}
853 	      else /* dst_alpha != 0) */
854 		{
855 		  dst_save_mul = 0xff0000 / dst_alpha;
856 		}
857 
858 	      for (j = 0; j < 3; j++)
859 		{
860 		  art_u32 src, dst;
861 		  art_u32 tmp;
862 
863 		  src = (bufptr[j] * src_mul + 0x8000) >> 16;
864 		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
865 		  tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
866 		  tmp -= tmp >> 16;
867 		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
868 		}
869 	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
870 
871 	      bufptr += 4;
872 	      dstptr += 4;
873 	    }
874 	}
875     }
876 }
877 
878 const ArtRenderCallback art_render_composite_8_opt2_obj =
879 {
880   art_render_composite_8_opt2,
881   art_render_nop_done
882 };
883 
884 
885 /* todo: inline */
886 static ArtRenderCallback *
art_render_choose_compositing_callback(ArtRender * render)887 art_render_choose_compositing_callback (ArtRender *render)
888 {
889   if (render->depth == 8 && render->buf_depth == 8)
890     {
891       if (render->n_chan == 3 &&
892 	  render->alpha_buf == NULL &&
893 	  render->alpha_type == ART_ALPHA_SEPARATE)
894 	{
895 	  if (render->buf_alpha == ART_ALPHA_NONE)
896 	    return (ArtRenderCallback *)&art_render_composite_8_opt1_obj;
897 	  else if (render->buf_alpha == ART_ALPHA_PREMUL)
898 	    return (ArtRenderCallback *)&art_render_composite_8_opt2_obj;
899 	}
900 
901       return (ArtRenderCallback *)&art_render_composite_8_obj;
902     }
903   return (ArtRenderCallback *)&art_render_composite_obj;
904 }
905 
906 /**
907  * art_render_invoke_callbacks: Invoke the callbacks in the render object.
908  * @render: The render object.
909  * @y: The current Y coordinate value.
910  *
911  * Invokes the callbacks of the render object in the appropriate
912  * order.  Drivers should call this routine once per scanline.
913  *
914  * todo: should management of dest devolve to this routine? very
915  * plausibly yes.
916  **/
917 void
art_render_invoke_callbacks(ArtRender * render,art_u8 * dest,int y)918 art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y)
919 {
920   ArtRenderPriv *priv = (ArtRenderPriv *)render;
921   int i;
922 
923   for (i = 0; i < priv->n_callbacks; i++)
924     {
925       ArtRenderCallback *callback;
926 
927       callback = priv->callbacks[i];
928       callback->render (callback, render, dest, y);
929     }
930 }
931 
932 /**
933  * art_render_invoke: Perform the requested rendering task.
934  * @render: The render object.
935  *
936  * Invokes the renderer and all sources associated with it, to perform
937  * the requested rendering task.
938  **/
939 void
art_render_invoke(ArtRender * render)940 art_render_invoke (ArtRender *render)
941 {
942   ArtRenderPriv *priv = (ArtRenderPriv *)render;
943   int width;
944   int best_driver, best_score;
945   int i;
946   int n_callbacks, n_callbacks_max;
947   ArtImageSource *image_source;
948   ArtImageSourceFlags image_flags;
949   int buf_depth;
950   ArtAlphaType buf_alpha;
951   art_boolean first = ART_TRUE;
952 
953   if (render == NULL)
954     {
955       art_warn ("art_render_invoke: called with render == NULL\n");
956       return;
957     }
958   if (priv->image_source == NULL)
959     {
960       art_warn ("art_render_invoke: no image source given\n");
961       return;
962     }
963 
964   width = render->x1 - render->x0;
965 
966   render->run = art_new (ArtRenderMaskRun, width + 1);
967 
968   /* Elect a mask source as driver. */
969   best_driver = -1;
970   best_score = 0;
971   for (i = 0; i < priv->n_mask_source; i++)
972     {
973       int score;
974       ArtMaskSource *mask_source;
975 
976       mask_source = priv->mask_source[i];
977       score = mask_source->can_drive (mask_source, render);
978       if (score > best_score)
979 	{
980 	  best_score = score;
981 	  best_driver = i;
982 	}
983     }
984 
985   /* Allocate alpha buffer if needed. */
986   if (priv->n_mask_source > 1 ||
987       (priv->n_mask_source == 1 && best_driver < 0))
988     {
989       render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3);
990     }
991 
992   /* Negotiate image rendering and compositing. */
993   image_source = priv->image_source;
994   image_source->negotiate (image_source, render, &image_flags, &buf_depth,
995 			   &buf_alpha);
996 
997   /* Build callback list. */
998   n_callbacks_max = priv->n_mask_source + 3;
999   priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max);
1000   n_callbacks = 0;
1001   for (i = 0; i < priv->n_mask_source; i++)
1002     if (i != best_driver)
1003       {
1004 	ArtMaskSource *mask_source = priv->mask_source[i];
1005 
1006 	mask_source->prepare (mask_source, render, first);
1007 	first = ART_FALSE;
1008 	priv->callbacks[n_callbacks++] = &mask_source->super;
1009       }
1010 
1011   if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR))
1012     priv->callbacks[n_callbacks++] =
1013       art_render_choose_clear_callback (render);
1014 
1015   priv->callbacks[n_callbacks++] = &image_source->super;
1016 
1017   /* Allocate image buffer and add compositing callback if needed. */
1018   if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE))
1019     {
1020       int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) *
1021 		     buf_depth) >> 3;
1022       render->buf_depth = buf_depth;
1023       render->buf_alpha = buf_alpha;
1024       render->image_buf = art_new (art_u8, width * bytespp);
1025       priv->callbacks[n_callbacks++] =
1026 	art_render_choose_compositing_callback (render);
1027     }
1028 
1029   priv->n_callbacks = n_callbacks;
1030 
1031   if (render->need_span)
1032     render->span_x = art_new (int, width + 1);
1033 
1034   /* Invoke the driver */
1035   if (best_driver >= 0)
1036     {
1037       ArtMaskSource *driver;
1038 
1039       driver = priv->mask_source[best_driver];
1040       driver->invoke_driver (driver, render);
1041     }
1042   else
1043     {
1044       art_u8 *dest_ptr = render->pixels;
1045       int y;
1046 
1047       /* Dummy driver */
1048       render->n_run = 2;
1049       render->run[0].x = render->x0;
1050       render->run[0].alpha = 0x8000 + 0xff * render->opacity;
1051       render->run[1].x = render->x1;
1052       render->run[1].alpha = 0x8000;
1053       if (render->need_span)
1054 	{
1055 	  render->n_span = 2;
1056 	  render->span_x[0] = render->x0;
1057 	  render->span_x[1] = render->x1;
1058 	}
1059       for (y = render->y0; y < render->y1; y++)
1060 	{
1061 	  art_render_invoke_callbacks (render, dest_ptr, y);
1062 	  dest_ptr += render->rowstride;
1063 	}
1064     }
1065 
1066   if (priv->mask_source != NULL)
1067     art_free (priv->mask_source);
1068 
1069   /* clean up callbacks */
1070   for (i = 0; i < priv->n_callbacks; i++)
1071     {
1072       ArtRenderCallback *callback;
1073 
1074       callback = priv->callbacks[i];
1075       callback->done (callback, render);
1076     }
1077 
1078   /* Tear down object */
1079   if (render->alpha_buf != NULL)
1080     art_free (render->alpha_buf);
1081   if (render->image_buf != NULL)
1082     art_free (render->image_buf);
1083   art_free (render->run);
1084   if (render->span_x != NULL)
1085     art_free (render->span_x);
1086   art_free (priv->callbacks);
1087   art_free (render);
1088 }
1089 
1090 /**
1091  * art_render_mask_solid: Add a solid translucent mask.
1092  * @render: The render object.
1093  * @opacity: Opacity in [0..0x10000] form.
1094  *
1095  * Adds a translucent mask to the rendering object.
1096  **/
1097 void
art_render_mask_solid(ArtRender * render,int opacity)1098 art_render_mask_solid (ArtRender *render, int opacity)
1099 {
1100   art_u32 old_opacity = render->opacity;
1101   art_u32 new_opacity_tmp;
1102 
1103   if (opacity == 0x10000)
1104     /* avoid potential overflow */
1105     return;
1106   new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000;
1107   render->opacity = new_opacity_tmp >> 16;
1108 }
1109 
1110 /**
1111  * art_render_add_mask_source: Add a mask source to the render object.
1112  * @render: Render object.
1113  * @mask_source: Mask source to add.
1114  *
1115  * This routine adds a mask source to the render object. In general,
1116  * client api's for adding mask sources should just take a render object,
1117  * then the mask source creation function should call this function.
1118  * Clients should never have to call this function directly, unless of
1119  * course they're creating custom mask sources.
1120  **/
1121 void
art_render_add_mask_source(ArtRender * render,ArtMaskSource * mask_source)1122 art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source)
1123 {
1124   ArtRenderPriv *priv = (ArtRenderPriv *)render;
1125   int n_mask_source = priv->n_mask_source++;
1126 
1127   if (n_mask_source == 0)
1128     priv->mask_source = art_new (ArtMaskSource *, 1);
1129   /* This predicate is true iff n_mask_source is a power of two */
1130   else if (!(n_mask_source & (n_mask_source - 1)))
1131     priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *,
1132 				   n_mask_source << 1);
1133 
1134   priv->mask_source[n_mask_source] = mask_source;
1135 }
1136 
1137 /**
1138  * art_render_add_image_source: Add a mask source to the render object.
1139  * @render: Render object.
1140  * @image_source: Image source to add.
1141  *
1142  * This routine adds an image source to the render object. In general,
1143  * client api's for adding image sources should just take a render
1144  * object, then the mask source creation function should call this
1145  * function.  Clients should never have to call this function
1146  * directly, unless of course they're creating custom image sources.
1147  **/
1148 void
art_render_add_image_source(ArtRender * render,ArtImageSource * image_source)1149 art_render_add_image_source (ArtRender *render, ArtImageSource *image_source)
1150 {
1151   ArtRenderPriv *priv = (ArtRenderPriv *)render;
1152 
1153   if (priv->image_source != NULL)
1154     {
1155       art_warn ("art_render_add_image_source: image source already present.\n");
1156       return;
1157     }
1158   priv->image_source = image_source;
1159 }
1160 
1161 /* Solid image source object and methods. Perhaps this should go into a
1162    separate file. */
1163 
1164 typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
1165 
1166 struct _ArtImageSourceSolid {
1167   ArtImageSource super;
1168   ArtPixMaxDepth color[ART_MAX_CHAN];
1169   art_u32 *rgbtab;
1170   art_boolean init;
1171 };
1172 
1173 static void
art_render_image_solid_done(ArtRenderCallback * self,ArtRender * render)1174 art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render)
1175 {
1176   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1177 
1178   if (z->rgbtab != NULL)
1179     art_free (z->rgbtab);
1180   art_free (self);
1181 }
1182 
1183 static void
art_render_image_solid_rgb8_opaq_init(ArtImageSourceSolid * self,ArtRender * render)1184 art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render)
1185 {
1186   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1187   ArtPixMaxDepth color_max;
1188   int r_fg, g_fg, b_fg;
1189   int r_bg, g_bg, b_bg;
1190   int r, g, b;
1191   int dr, dg, db;
1192   int i;
1193   int tmp;
1194   art_u32 *rgbtab;
1195 
1196   rgbtab = art_new (art_u32, 256);
1197   z->rgbtab = rgbtab;
1198 
1199   color_max = self->color[0];
1200   r_fg = ART_PIX_8_FROM_MAX (color_max);
1201   color_max = self->color[1];
1202   g_fg = ART_PIX_8_FROM_MAX (color_max);
1203   color_max = self->color[2];
1204   b_fg = ART_PIX_8_FROM_MAX (color_max);
1205 
1206   color_max = render->clear_color[0];
1207   r_bg = ART_PIX_8_FROM_MAX (color_max);
1208   color_max = render->clear_color[1];
1209   g_bg = ART_PIX_8_FROM_MAX (color_max);
1210   color_max = render->clear_color[2];
1211   b_bg = ART_PIX_8_FROM_MAX (color_max);
1212 
1213   r = (r_bg << 16) + 0x8000;
1214   g = (g_bg << 16) + 0x8000;
1215   b = (b_bg << 16) + 0x8000;
1216   tmp = ((r_fg - r_bg) << 16) + 0x80;
1217   dr = (tmp + (tmp >> 8)) >> 8;
1218   tmp = ((g_fg - g_bg) << 16) + 0x80;
1219   dg = (tmp + (tmp >> 8)) >> 8;
1220   tmp = ((b_fg - b_bg) << 16) + 0x80;
1221   db = (tmp + (tmp >> 8)) >> 8;
1222 
1223   for (i = 0; i < 256; i++)
1224     {
1225       rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
1226       r += dr;
1227       g += dg;
1228       b += db;
1229     }
1230 }
1231 
1232 static void
art_render_image_solid_rgb8_opaq(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)1233 art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render,
1234 				  art_u8 *dest, int y)
1235 {
1236   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1237   ArtRenderMaskRun *run = render->run;
1238   int n_run = render->n_run;
1239   art_u32 *rgbtab = z->rgbtab;
1240   art_u32 rgb;
1241   int x0 = render->x0;
1242   int x1 = render->x1;
1243   int run_x0, run_x1;
1244   int i;
1245   int ix;
1246 
1247   if (n_run > 0)
1248     {
1249       run_x1 = run[0].x;
1250       if (run_x1 > x0)
1251 	{
1252 	  rgb = rgbtab[0];
1253 	  art_rgb_fill_run (dest,
1254 			    rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1255 			    run_x1 - x0);
1256 	}
1257       for (i = 0; i < n_run - 1; i++)
1258 	{
1259 	  run_x0 = run_x1;
1260 	  run_x1 = run[i + 1].x;
1261 	  rgb = rgbtab[(run[i].alpha >> 16) & 0xff];
1262 	  ix = (run_x0 - x0) * 3;
1263 #define OPTIMIZE_LEN_1
1264 #ifdef OPTIMIZE_LEN_1
1265 	  if (run_x1 - run_x0 == 1)
1266 	    {
1267 	      dest[ix] = rgb >> 16;
1268 	      dest[ix + 1] = (rgb >> 8) & 0xff;
1269 	      dest[ix + 2] = rgb & 0xff;
1270 	    }
1271 	  else
1272 	    {
1273 	      art_rgb_fill_run (dest + ix,
1274 				rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1275 				run_x1 - run_x0);
1276 	    }
1277 #else
1278 	  art_rgb_fill_run (dest + ix,
1279 			    rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1280 			    run_x1 - run_x0);
1281 #endif
1282 	}
1283     }
1284   else
1285     {
1286       run_x1 = x0;
1287     }
1288   if (run_x1 < x1)
1289     {
1290       rgb = rgbtab[0];
1291       art_rgb_fill_run (dest + (run_x1 - x0) * 3,
1292 			rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1293 			x1 - run_x1);
1294     }
1295 }
1296 
1297 static void
art_render_image_solid_rgb8(ArtRenderCallback * self,ArtRender * render,art_u8 * dest,int y)1298 art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render,
1299 			     art_u8 *dest, int y)
1300 {
1301   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1302   int width = render->x1 - render->x0;
1303   art_u8 r, g, b;
1304   ArtPixMaxDepth color_max;
1305 
1306   /* todo: replace this simple test with real sparseness */
1307   if (z->init)
1308     return;
1309   z->init = ART_TRUE;
1310 
1311   color_max = z->color[0];
1312   r = ART_PIX_8_FROM_MAX (color_max);
1313   color_max = z->color[1];
1314   g = ART_PIX_8_FROM_MAX (color_max);
1315   color_max = z->color[2];
1316   b = ART_PIX_8_FROM_MAX (color_max);
1317 
1318   art_rgb_fill_run (render->image_buf, r, g, b, width);
1319 }
1320 
1321 static void
art_render_image_solid_negotiate(ArtImageSource * self,ArtRender * render,ArtImageSourceFlags * p_flags,int * p_buf_depth,ArtAlphaType * p_alpha)1322 art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render,
1323 				  ArtImageSourceFlags *p_flags,
1324 				  int *p_buf_depth, ArtAlphaType *p_alpha)
1325 {
1326   ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
1327   ArtImageSourceFlags flags = 0;
1328   static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render,
1329 			     art_u8 *dest, int y);
1330 
1331   render_cbk = NULL;
1332 
1333   if (render->depth == 8 && render->n_chan == 3 &&
1334       render->alpha_type == ART_ALPHA_NONE)
1335     {
1336       if (render->clear)
1337 	{
1338 	  render_cbk = art_render_image_solid_rgb8_opaq;
1339 	  flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE;
1340 	  art_render_image_solid_rgb8_opaq_init (z, render);
1341 	}
1342     }
1343   if (render_cbk == NULL)
1344     {
1345       if (render->depth == 8)
1346 	{
1347 	  render_cbk = art_render_image_solid_rgb8;
1348 	  *p_buf_depth = 8;
1349 	  *p_alpha = ART_ALPHA_NONE; /* todo */
1350 	}
1351     }
1352   /* todo: general case */
1353   self->super.render = render_cbk;
1354   *p_flags = flags;
1355 }
1356 
1357 /**
1358  * art_render_image_solid: Add a solid color image source.
1359  * @render: The render object.
1360  * @color: Color.
1361  *
1362  * Adds an image source with the solid color given by @color. The
1363  * color need not be retained in memory after this call.
1364  **/
1365 void
art_render_image_solid(ArtRender * render,ArtPixMaxDepth * color)1366 art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color)
1367 {
1368   ArtImageSourceSolid *image_source;
1369   int i;
1370 
1371   image_source = art_new (ArtImageSourceSolid, 1);
1372   image_source->super.super.render = NULL;
1373   image_source->super.super.done = art_render_image_solid_done;
1374   image_source->super.negotiate = art_render_image_solid_negotiate;
1375 
1376   for (i = 0; i < render->n_chan; i++)
1377     image_source->color[i] = color[i];
1378 
1379   image_source->rgbtab = NULL;
1380   image_source->init = ART_FALSE;
1381 
1382   art_render_add_image_source (render, &image_source->super);
1383 }
1384