source: trunk/third/libart_lgpl/art_render.c @ 20813

Revision 20813, 34.7 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20812, which included commits to RCS files with non-trunk default branches.
Line 
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
28typedef struct _ArtRenderPriv ArtRenderPriv;
29
30struct _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
42ArtRender *
43art_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 **/
113void
114art_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 **/
131void
132art_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
151static void
152art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
153{
154}
155
156static void
157art_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
174static void
175art_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
196const ArtRenderCallback art_render_clear_rgb8_obj =
197{
198  art_render_clear_render_rgb8,
199  art_render_nop_done
200};
201
202const 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
210static void
211art_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
233const 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 */
242static ArtRenderCallback *
243art_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 */
269static void
270art_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. */
282static void
283art_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
497const ArtRenderCallback art_render_composite_obj =
498{
499  art_render_composite,
500  art_render_nop_done
501};
502
503static void
504art_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
640const 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 */
653static void
654art_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
737const 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 */
749static void
750art_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
878const 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 */
886static ArtRenderCallback *
887art_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 **/
917void
918art_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 **/
939void
940art_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 **/
1097void
1098art_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 **/
1121void
1122art_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 **/
1148void
1149art_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
1164typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
1165
1166struct _ArtImageSourceSolid {
1167  ArtImageSource super;
1168  ArtPixMaxDepth color[ART_MAX_CHAN];
1169  art_u32 *rgbtab;
1170  art_boolean init;
1171};
1172
1173static void
1174art_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
1183static void
1184art_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
1232static void
1233art_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
1297static void
1298art_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
1321static void
1322art_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 **/
1365void
1366art_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}
Note: See TracBrowser for help on using the repository browser.