source: trunk/third/mozilla/other-licenses/libart_lgpl/art_render.c @ 22325

Revision 22325, 34.6 KB checked in by rbasch, 19 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r22324, 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 *)bufptr)[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                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          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
639const 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 */
652static void
653art_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
736const 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 */
748static void
749art_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
877const 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 */
885static ArtRenderCallback *
886art_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 **/
916void
917art_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 **/
938void
939art_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 **/
1096void
1097art_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 **/
1120void
1121art_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 **/
1147void
1148art_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
1163typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
1164
1165struct _ArtImageSourceSolid {
1166  ArtImageSource super;
1167  ArtPixMaxDepth color[ART_MAX_CHAN];
1168  art_u32 *rgbtab;
1169  art_boolean init;
1170};
1171
1172static void
1173art_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
1182static void
1183art_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
1231static void
1232art_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                            rgb >> 16, (rgb >> 8) & 0xff, 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                                rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1274                                run_x1 - run_x0);
1275            }
1276#else
1277          art_rgb_fill_run (dest + ix,
1278                            rgb >> 16, (rgb >> 8) & 0xff, 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                        rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
1292                        x1 - run_x1);
1293    }
1294}
1295
1296static void
1297art_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
1320static void
1321art_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 **/
1364void
1365art_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}
Note: See TracBrowser for help on using the repository browser.