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