1cb96a6086e840f924c0eb888b594254676563e7
[rrdtool.git] / src / art_rgba_svp.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  art_rgba_svp.c: A slightly modified version of art_rgb_svp to render into rgba buffer
4  *
5  *  This program is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Library General Public License
7  *  as published by the Free Software Foundation; either version 2 of
8  *  the License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Library General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Library General Public
16  *  License along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  *  Authors:
20  *    Raph Levien <raph@acm.org>
21  *    Lauris Kaplinski <lauris@ariman.ee>
22  *
23  *  Copyright (C) 1998 Raph Levien
24  *
25  */
26
27 #define SP_ART_RGBA_SVP_C
28
29 /* Render a sorted vector path into an RGBA buffer. */
30
31 #include <libart_lgpl/art_misc.h>
32 #include <libart_lgpl/art_svp.h>
33 #include <libart_lgpl/art_svp_render_aa.h>
34 #include <libart_lgpl/art_rgb.h>
35
36 #include "art_rgba_svp.h"
37 #include "unused.h"
38
39 static void art_rgba_fill_run(
40     art_u8 * linebuf,
41     art_u8 r,
42     art_u8 g,
43     art_u8 b,
44     int n);
45 static void art_rgba_run_alpha(
46     art_u8 * linebuf,
47     art_u8 r,
48     art_u8 g,
49     art_u8 b,
50     int alpha,
51     int n);
52
53 typedef struct _ArtRgbaSVPAlphaData ArtRgbaSVPAlphaData;
54
55 struct _ArtRgbaSVPAlphaData {
56     int       alphatab[256];
57     art_u8    r, g, b, alpha;
58     art_u8   *buf;
59     int       rowstride;
60     int       libart_x0, libart_x1;
61 };
62
63 static void art_rgba_svp_alpha_callback(
64     void *callback_data,
65     int UNUSED(y),
66     int start,
67     ArtSVPRenderAAStep * steps,
68     int n_steps)
69 {
70     ArtRgbaSVPAlphaData *data = callback_data;
71     art_u8   *linebuf;
72     int       run_x0, run_x1;
73     art_u32   running_sum = start;
74     int       libart_x0, libart_x1;
75     int       k;
76     art_u8    r, g, b;
77     int      *alphatab;
78     int       alpha;
79
80     linebuf = data->buf;
81     libart_x0 = data->libart_x0;
82     libart_x1 = data->libart_x1;
83
84     r = data->r;
85     g = data->g;
86     b = data->b;
87     alphatab = data->alphatab;
88
89     if (n_steps > 0) {
90         run_x1 = steps[0].x;
91         if (run_x1 > libart_x0) {
92             alpha = (running_sum >> 16) & 0xff;
93             if (alpha)
94                 art_rgba_run_alpha(linebuf,
95                                    r, g, b, alphatab[alpha],
96                                    run_x1 - libart_x0);
97         }
98
99         /* render the steps into tmpbuf */
100         for (k = 0; k < n_steps - 1; k++) {
101             running_sum += steps[k].delta;
102             run_x0 = run_x1;
103             run_x1 = steps[k + 1].x;
104             if (run_x1 > run_x0) {
105                 alpha = (running_sum >> 16) & 0xff;
106                 if (alpha)
107                     art_rgba_run_alpha(linebuf + (run_x0 - libart_x0) * 4,
108                                        r, g, b, alphatab[alpha],
109                                        run_x1 - run_x0);
110             }
111         }
112         running_sum += steps[k].delta;
113         if (libart_x1 > run_x1) {
114             alpha = (running_sum >> 16) & 0xff;
115             if (alpha)
116                 art_rgba_run_alpha(linebuf + (run_x1 - libart_x0) * 4,
117                                    r, g, b, alphatab[alpha],
118                                    libart_x1 - run_x1);
119         }
120     } else {
121         alpha = (running_sum >> 16) & 0xff;
122         if (alpha)
123             art_rgba_run_alpha(linebuf,
124                                r, g, b, alphatab[alpha],
125                                libart_x1 - libart_x0);
126     }
127
128     data->buf += data->rowstride;
129 }
130
131 static void art_rgba_svp_alpha_opaque_callback(
132     void *callback_data,
133     int UNUSED(y),
134     int start,
135     ArtSVPRenderAAStep * steps,
136     int n_steps)
137 {
138     ArtRgbaSVPAlphaData *data = callback_data;
139     art_u8   *linebuf;
140     int       run_x0, run_x1;
141     art_u32   running_sum = start;
142     int       libart_x0, libart_x1;
143     int       k;
144     art_u8    r, g, b;
145     int      *alphatab;
146     int       alpha;
147
148     linebuf = data->buf;
149     libart_x0 = data->libart_x0;
150     libart_x1 = data->libart_x1;
151
152     r = data->r;
153     g = data->g;
154     b = data->b;
155     alphatab = data->alphatab;
156
157     if (n_steps > 0) {
158         run_x1 = steps[0].x;
159         if (run_x1 > libart_x0) {
160             alpha = running_sum >> 16;
161             if (alpha) {
162                 if (alpha >= 255)
163                     art_rgba_fill_run(linebuf, r, g, b, run_x1 - libart_x0);
164                 else
165                     art_rgba_run_alpha(linebuf,
166                                        r, g, b, alphatab[alpha],
167                                        run_x1 - libart_x0);
168             }
169         }
170
171         /* render the steps into tmpbuf */
172         for (k = 0; k < n_steps - 1; k++) {
173             running_sum += steps[k].delta;
174             run_x0 = run_x1;
175             run_x1 = steps[k + 1].x;
176             if (run_x1 > run_x0) {
177                 alpha = running_sum >> 16;
178                 if (alpha) {
179                     if (alpha >= 255)
180                         art_rgba_fill_run(linebuf + (run_x0 - libart_x0) * 4,
181                                           r, g, b, run_x1 - run_x0);
182                     else
183                         art_rgba_run_alpha(linebuf + (run_x0 - libart_x0) * 4,
184                                            r, g, b, alphatab[alpha],
185                                            run_x1 - run_x0);
186                 }
187             }
188         }
189         running_sum += steps[k].delta;
190         if (libart_x1 > run_x1) {
191             alpha = running_sum >> 16;
192             if (alpha) {
193                 if (alpha >= 255)
194                     art_rgba_fill_run(linebuf + (run_x1 - libart_x0) * 4,
195                                       r, g, b, libart_x1 - run_x1);
196                 else
197                     art_rgba_run_alpha(linebuf + (run_x1 - libart_x0) * 4,
198                                        r, g, b, alphatab[alpha],
199                                        libart_x1 - run_x1);
200             }
201         }
202     } else {
203         alpha = running_sum >> 16;
204         if (alpha) {
205             if (alpha >= 255)
206                 art_rgba_fill_run(linebuf, r, g, b, libart_x1 - libart_x0);
207             else
208                 art_rgba_run_alpha(linebuf,
209                                    r, g, b, alphatab[alpha],
210                                    libart_x1 - libart_x0);
211         }
212     }
213
214     data->buf += data->rowstride;
215 }
216
217 /**
218  * gnome_print_art_rgba_svp_alpha: Alpha-composite sorted vector path over RGBA buffer.
219  * @svp: The source sorted vector path.
220  * @libart_x0: Left coordinate of destination rectangle.
221  * @libart_y0: Top coordinate of destination rectangle.
222  * @libart_x1: Right coordinate of destination rectangle.
223  * @libart_y1: Bottom coordinate of destination rectangle.
224  * @rgba: Color in 0xRRGGBBAA format.
225  * @buf: Destination RGB buffer.
226  * @rowstride: Rowstride of @buf buffer.
227  * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
228  *
229  * Renders the shape specified with @svp over the @buf RGB buffer.
230  * @libart_x1 - @x0 specifies the width, and @libart_y1 - @libart_y0 specifies the height,
231  * of the rectangle rendered. The new pixels are stored starting at
232  * the first byte of @buf. Thus, the @x0 and @libart_y0 parameters specify
233  * an offset within @svp, and may be tweaked as a way of doing
234  * integer-pixel translations without fiddling with @svp itself.
235  *
236  * The @rgba argument specifies the color for the rendering. Pixels of
237  * entirely 0 winding number are left untouched. Pixels of entirely
238  * 1 winding number have the color @rgba composited over them (ie,
239  * are replaced by the red, green, blue components of @rgba if the alpha
240  * component is 0xff). Pixels of intermediate coverage are interpolated
241  * according to the rule in @alphagamma, or default to linear if
242  * @alphagamma is NULL.
243  **/
244 void gnome_print_art_rgba_svp_alpha(
245     const ArtSVP * svp,
246     int libart_x0,
247     int libart_y0,
248     int libart_x1,
249     int libart_y1,
250     art_u32 rgba,
251     art_u8 * buf,
252     int rowstride,
253     ArtAlphaGamma UNUSED(*alphagamma))
254 {
255     ArtRgbaSVPAlphaData data;
256     int       r, g, b, alpha;
257     int       i;
258     int       a, da;
259
260     r = rgba >> 24;
261     g = (rgba >> 16) & 0xff;
262     b = (rgba >> 8) & 0xff;
263     alpha = rgba & 0xff;
264
265     data.r = r;
266     data.g = g;
267     data.b = b;
268     data.alpha = alpha;
269
270     a = 0x8000;
271     da = (alpha * 66051 + 0x80) >> 8;   /* 66051 equals 2 ^ 32 / (255 * 255) */
272
273     for (i = 0; i < 256; i++) {
274         data.alphatab[i] = a >> 16;
275         a += da;
276     }
277
278     data.buf = buf;
279     data.rowstride = rowstride;
280     data.libart_x0 = libart_x0;
281     data.libart_x1 = libart_x1;
282     if (alpha == 255)
283         art_svp_render_aa(svp, libart_x0, libart_y0, libart_x1, libart_y1,
284                           art_rgba_svp_alpha_opaque_callback, &data);
285     else
286         art_svp_render_aa(svp, libart_x0, libart_y0, libart_x1, libart_y1,
287                           art_rgba_svp_alpha_callback, &data);
288 }
289
290 static void art_rgba_fill_run(
291     art_u8 * buf,
292     art_u8 r,
293     art_u8 g,
294     art_u8 b,
295     int n)
296 {
297     int       i;
298
299     for (i = 0; i < n; i++) {
300         *buf++ = r;
301         *buf++ = g;
302         *buf++ = b;
303         *buf++ = 255;
304     }
305 }
306
307 /* fixme: this */
308
309 static void art_rgba_run_alpha(
310     art_u8 * buf,
311     art_u8 r,
312     art_u8 g,
313     art_u8 b,
314     int alpha,
315     int n)
316 {
317     int       i;
318     int       br, bg, bb, ba;
319     int       cr, cg, cb;
320
321     for (i = 0; i < n; i++) {
322         br = *(buf + 0);
323         bg = *(buf + 1);
324         bb = *(buf + 2);
325         ba = *(buf + 3);
326
327         cr = (br * ba + 0x80) >> 8;
328         cg = (bg * ba + 0x80) >> 8;
329         cb = (bb * ba + 0x80) >> 8;
330
331         *buf++ = cr + (((r - cr) * alpha + 0x80) >> 8);
332         *buf++ = cg + (((g - cg) * alpha + 0x80) >> 8);
333         *buf++ = cb + (((b - cb) * alpha + 0x80) >> 8);
334         *buf++ = ba + (((255 - ba) * alpha + 0x80) >> 8);
335     }
336 }