X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fart%2Fart_render.c;fp=lib%2Fart%2Fart_render.c;h=65b344cc5634224ddcef258e604a83d39c672cba;hb=418ef05b5d0b74e6f19200ffa40bcc3afccb5029;hp=0000000000000000000000000000000000000000;hpb=c1f4a906117e98e2f4ba59abc1ce153e8129bc19;p=swftools.git diff --git a/lib/art/art_render.c b/lib/art/art_render.c new file mode 100644 index 0000000..65b344c --- /dev/null +++ b/lib/art/art_render.c @@ -0,0 +1,1383 @@ +/* + * art_render.c: Modular rendering architecture. + * + * Libart_LGPL - library of basic graphic primitives + * Copyright (C) 2000 Raph Levien + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "art_render.h" + +#include "art_rgb.h" + +typedef struct _ArtRenderPriv ArtRenderPriv; + +struct _ArtRenderPriv { + ArtRender super; + + ArtImageSource *image_source; + + int n_mask_source; + ArtMaskSource **mask_source; + + int n_callbacks; + ArtRenderCallback **callbacks; +}; + +ArtRender * +art_render_new (int x0, int y0, int x1, int y1, + art_u8 *pixels, int rowstride, + int n_chan, int depth, ArtAlphaType alpha_type, + ArtAlphaGamma *alphagamma) +{ + ArtRenderPriv *priv; + ArtRender *result; + + priv = art_new (ArtRenderPriv, 1); + result = &priv->super; + + if (n_chan > ART_MAX_CHAN) + { + art_warn ("art_render_new: n_chan = %d, exceeds %d max\n", + n_chan, ART_MAX_CHAN); + return NULL; + } + if (depth > ART_MAX_DEPTH) + { + art_warn ("art_render_new: depth = %d, exceeds %d max\n", + depth, ART_MAX_DEPTH); + return NULL; + } + if (x0 >= x1) + { + art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1); + return NULL; + } + result->x0 = x0; + result->y0 = y0; + result->x1 = x1; + result->y1 = y1; + result->pixels = pixels; + result->rowstride = rowstride; + result->n_chan = n_chan; + result->depth = depth; + result->alpha_type = alpha_type; + + result->clear = ART_FALSE; + result->opacity = 0x10000; + result->compositing_mode = ART_COMPOSITE_NORMAL; + result->alphagamma = alphagamma; + + result->alpha_buf = NULL; + result->image_buf = NULL; + + result->run = NULL; + result->span_x = NULL; + + result->need_span = ART_FALSE; + + priv->image_source = NULL; + + priv->n_mask_source = 0; + priv->mask_source = NULL; + + return result; +} + +/* todo on clear routines: I haven't really figured out what to do + with clearing the alpha channel. It _should_ be possible to clear + to an arbitrary RGBA color. */ + +/** + * art_render_clear: Set clear color. + * @clear_color: Color with which to clear dest. + * + * Sets clear color, equivalent to actually clearing the destination + * buffer before rendering. This is the most general form. + **/ +void +art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color) +{ + int i; + int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); + + render->clear = ART_TRUE; + for (i = 0; i < n_ch; i++) + render->clear_color[i] = clear_color[i]; +} + +/** + * art_render_clear_rgb: Set clear color, given in RGB format. + * @clear_rgb: Clear color, in 0xRRGGBB format. + * + * Sets clear color, equivalent to actually clearing the destination + * buffer before rendering. + **/ +void +art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb) +{ + if (render->n_chan != 3) + art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n", + render->n_chan); + else + { + int r, g, b; + + render->clear = ART_TRUE; + r = clear_rgb >> 16; + g = (clear_rgb >> 8) & 0xff; + b = clear_rgb & 0xff; + render->clear_color[0] = ART_PIX_MAX_FROM_8(r); + render->clear_color[1] = ART_PIX_MAX_FROM_8(g); + render->clear_color[2] = ART_PIX_MAX_FROM_8(b); + } +} + +static void +art_render_nop_done (ArtRenderCallback *self, ArtRender *render) +{ +} + +static void +art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + int width = render->x1 - render->x0; + art_u8 r, g, b; + ArtPixMaxDepth color_max; + + color_max = render->clear_color[0]; + r = ART_PIX_8_FROM_MAX (color_max); + color_max = render->clear_color[1]; + g = ART_PIX_8_FROM_MAX (color_max); + color_max = render->clear_color[2]; + b = ART_PIX_8_FROM_MAX (color_max); + + art_rgb_fill_run (dest, r, g, b, width); +} + +static void +art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + int width = render->x1 - render->x0; + int i, j; + int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); + int ix; + art_u8 color[ART_MAX_CHAN + 1]; + + for (j = 0; j < n_ch; j++) + { + ArtPixMaxDepth color_max = render->clear_color[j]; + color[j] = ART_PIX_8_FROM_MAX (color_max); + } + + ix = 0; + for (i = 0; i < width; i++) + for (j = 0; j < n_ch; j++) + dest[ix++] = color[j]; +} + +const ArtRenderCallback art_render_clear_rgb8_obj = +{ + art_render_clear_render_rgb8, + art_render_nop_done +}; + +const ArtRenderCallback art_render_clear_8_obj = +{ + art_render_clear_render_8, + art_render_nop_done +}; + +#if ART_MAX_DEPTH >= 16 + +static void +art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + int width = render->x1 - render->x0; + int i, j; + int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); + int ix; + art_u16 *dest_16 = (art_u16 *)dest; + art_u8 color[ART_MAX_CHAN + 1]; + + for (j = 0; j < n_ch; j++) + { + int color_16 = render->clear_color[j]; + color[j] = color_16; + } + + ix = 0; + for (i = 0; i < width; i++) + for (j = 0; j < n_ch; j++) + dest_16[ix++] = color[j]; +} + +const ArtRenderCallback art_render_clear_16_obj = +{ + art_render_clear_render_16, + art_render_nop_done +}; + +#endif /* ART_MAX_DEPTH >= 16 */ + +/* todo: inline */ +static ArtRenderCallback * +art_render_choose_clear_callback (ArtRender *render) +{ + ArtRenderCallback *clear_callback; + + if (render->depth == 8) + { + if (render->n_chan == 3 && + render->alpha_type == ART_ALPHA_NONE) + clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj; + else + clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj; + } +#if ART_MAX_DEPTH >= 16 + else if (render->depth == 16) + clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj; +#endif + else + { + art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n", + render->depth); + } + return clear_callback; +} + +#if 0 +/* todo: get around to writing this */ +static void +art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + int width = render->x1 - render->x0; + +} +#endif + +/* This is the most general form of the function. It is slow but + (hopefully) correct. Actually, I'm still worried about roundoff + errors in the premul case - it seems to me that an off-by-one could + lead to overflow. */ +static void +art_render_composite (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtRenderMaskRun *run = render->run; + art_u32 depth = render->depth; + int n_run = render->n_run; + int x0 = render->x0; + int x; + int run_x0, run_x1; + art_u8 *alpha_buf = render->alpha_buf; + art_u8 *image_buf = render->image_buf; + int i, j; + art_u32 tmp; + art_u32 run_alpha; + art_u32 alpha; + int image_ix; + art_u16 src[ART_MAX_CHAN + 1]; + art_u16 dst[ART_MAX_CHAN + 1]; + int n_chan = render->n_chan; + ArtAlphaType alpha_type = render->alpha_type; + int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE); + int dst_pixstride = n_ch * (depth >> 3); + int buf_depth = render->buf_depth; + ArtAlphaType buf_alpha = render->buf_alpha; + int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE); + int buf_pixstride = buf_n_ch * (buf_depth >> 3); + art_u8 *bufptr; + art_u32 src_alpha; + art_u32 src_mul; + art_u8 *dstptr; + art_u32 dst_alpha; + art_u32 dst_mul; + + image_ix = 0; + for (i = 0; i < n_run - 1; i++) + { + run_x0 = run[i].x; + run_x1 = run[i + 1].x; + tmp = run[i].alpha; + if (tmp < 0x8100) + continue; + + run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ + bufptr = image_buf + (run_x0 - x0) * buf_pixstride; + dstptr = dest + (run_x0 - x0) * dst_pixstride; + for (x = run_x0; x < run_x1; x++) + { + if (alpha_buf) + { + if (depth == 8) + { + tmp = run_alpha * alpha_buf[x - x0] + 0x80; + /* range 0x80 .. 0xff0080 */ + alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; + } + else /* (depth == 16) */ + { + tmp = ((art_u16 *)alpha_buf)[x - x0]; + tmp = (run_alpha * tmp + 0x8000) >> 8; + /* range 0x80 .. 0xffff80 */ + alpha = (tmp + (tmp >> 16)) >> 8; + } + } + else + alpha = run_alpha; + /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */ + + /* convert (src pixel * alpha) to premul alpha form, + store in src as 0..0xffff range */ + if (buf_alpha == ART_ALPHA_NONE) + { + src_alpha = alpha; + src_mul = src_alpha; + } + else + { + if (buf_depth == 8) + { + tmp = alpha * bufptr[n_chan] + 0x80; + /* range 0x80 .. 0xff0080 */ + src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; + } + else /* (depth == 16) */ + { + tmp = ((art_u16 *)bufptr)[n_chan]; + tmp = (alpha * tmp + 0x8000) >> 8; + /* range 0x80 .. 0xffff80 */ + src_alpha = (tmp + (tmp >> 16)) >> 8; + } + if (buf_alpha == ART_ALPHA_SEPARATE) + src_mul = src_alpha; + else /* buf_alpha == (ART_ALPHA_PREMUL) */ + src_mul = alpha; + } + /* src_alpha is the (alpha of the source pixel * alpha), + range 0..0x10000 */ + + if (buf_depth == 8) + { + src_mul *= 0x101; + for (j = 0; j < n_chan; j++) + src[j] = (bufptr[j] * src_mul + 0x8000) >> 16; + } + else if (buf_depth == 16) + { + for (j = 0; j < n_chan; j++) + src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16; + } + bufptr += buf_pixstride; + + /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range + 0..0x10000) now contain the source pixel with + premultiplied alpha */ + + /* convert dst pixel to premul alpha form, + store in dst as 0..0xffff range */ + if (alpha_type == ART_ALPHA_NONE) + { + dst_alpha = 0x10000; + dst_mul = dst_alpha; + } + else + { + if (depth == 8) + { + tmp = dstptr[n_chan]; + /* range 0..0xff */ + dst_alpha = (tmp << 8) + tmp + (tmp >> 7); + } + else /* (depth == 16) */ + { + tmp = ((art_u16 *)dstptr)[n_chan]; + dst_alpha = (tmp + (tmp >> 15)); + } + if (alpha_type == ART_ALPHA_SEPARATE) + dst_mul = dst_alpha; + else /* (alpha_type == ART_ALPHA_PREMUL) */ + dst_mul = 0x10000; + } + /* dst_alpha is the alpha of the dest pixel, + range 0..0x10000 */ + + if (depth == 8) + { + dst_mul *= 0x101; + for (j = 0; j < n_chan; j++) + dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16; + } + else if (buf_depth == 16) + { + for (j = 0; j < n_chan; j++) + dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16; + } + + /* do the compositing, dst = (src over dst) */ + for (j = 0; j < n_chan; j++) + { + art_u32 srcv, dstv; + art_u32 tmp; + + srcv = src[j]; + dstv = dst[j]; + tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv; + tmp -= tmp >> 16; + dst[j] = tmp; + } + + if (alpha_type == ART_ALPHA_NONE) + { + if (depth == 8) + dst_mul = 0xff; + else /* (depth == 16) */ + dst_mul = 0xffff; + } + else + { + if (src_alpha >= 0x10000) + dst_alpha = 0x10000; + else + dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; + if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0) + { + if (depth == 8) + dst_mul = 0xff; + else /* (depth == 16) */ + dst_mul = 0xffff; + } + else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */ + { + if (depth == 8) + dst_mul = 0xff0000 / dst_alpha; + else /* (depth == 16) */ + dst_mul = 0xffff0000 / dst_alpha; + } + } + if (depth == 8) + { + for (j = 0; j < n_chan; j++) + dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16; + if (alpha_type != ART_ALPHA_NONE) + dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16; + } + else if (depth == 16) + { + for (j = 0; j < n_chan; j++) + ((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16; + if (alpha_type != ART_ALPHA_NONE) + ((art_u16 *)dstptr)[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16; + } + dstptr += dst_pixstride; + } + } +} + +const ArtRenderCallback art_render_composite_obj = +{ + art_render_composite, + art_render_nop_done +}; + +static void +art_render_composite_8 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtRenderMaskRun *run = render->run; + int n_run = render->n_run; + int x0 = render->x0; + int x; + int run_x0, run_x1; + art_u8 *alpha_buf = render->alpha_buf; + art_u8 *image_buf = render->image_buf; + int i, j; + art_u32 tmp; + art_u32 run_alpha; + art_u32 alpha; + int image_ix; + int n_chan = render->n_chan; + ArtAlphaType alpha_type = render->alpha_type; + int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE); + int dst_pixstride = n_ch; + ArtAlphaType buf_alpha = render->buf_alpha; + int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE); + int buf_pixstride = buf_n_ch; + art_u8 *bufptr; + art_u32 src_alpha; + art_u32 src_mul; + art_u8 *dstptr; + art_u32 dst_alpha; + art_u32 dst_mul, dst_save_mul; + + image_ix = 0; + for (i = 0; i < n_run - 1; i++) + { + run_x0 = run[i].x; + run_x1 = run[i + 1].x; + tmp = run[i].alpha; + if (tmp < 0x10000) + continue; + + run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ + bufptr = image_buf + (run_x0 - x0) * buf_pixstride; + dstptr = dest + (run_x0 - x0) * dst_pixstride; + for (x = run_x0; x < run_x1; x++) + { + if (alpha_buf) + { + tmp = run_alpha * alpha_buf[x - x0] + 0x80; + /* range 0x80 .. 0xff0080 */ + alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; + } + else + alpha = run_alpha; + /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */ + + /* convert (src pixel * alpha) to premul alpha form, + store in src as 0..0xffff range */ + if (buf_alpha == ART_ALPHA_NONE) + { + src_alpha = alpha; + src_mul = src_alpha; + } + else + { + tmp = alpha * bufptr[n_chan] + 0x80; + /* range 0x80 .. 0xff0080 */ + src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; + + if (buf_alpha == ART_ALPHA_SEPARATE) + src_mul = src_alpha; + else /* buf_alpha == (ART_ALPHA_PREMUL) */ + src_mul = alpha; + } + /* src_alpha is the (alpha of the source pixel * alpha), + range 0..0x10000 */ + + src_mul *= 0x101; + + if (alpha_type == ART_ALPHA_NONE) + { + dst_alpha = 0x10000; + dst_mul = dst_alpha; + } + else + { + tmp = dstptr[n_chan]; + /* range 0..0xff */ + dst_alpha = (tmp << 8) + tmp + (tmp >> 7); + if (alpha_type == ART_ALPHA_SEPARATE) + dst_mul = dst_alpha; + else /* (alpha_type == ART_ALPHA_PREMUL) */ + dst_mul = 0x10000; + } + /* dst_alpha is the alpha of the dest pixel, + range 0..0x10000 */ + + dst_mul *= 0x101; + + if (alpha_type == ART_ALPHA_NONE) + { + dst_save_mul = 0xff; + } + else + { + if (src_alpha >= 0x10000) + dst_alpha = 0x10000; + else + dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; + if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0) + { + dst_save_mul = 0xff; + } + else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */ + { + dst_save_mul = 0xff0000 / dst_alpha; + } + } + + for (j = 0; j < n_chan; j++) + { + art_u32 src, dst; + art_u32 tmp; + + src = (bufptr[j] * src_mul + 0x8000) >> 16; + dst = (dstptr[j] * dst_mul + 0x8000) >> 16; + tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src; + tmp -= tmp >> 16; + dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; + } + if (alpha_type != ART_ALPHA_NONE) + dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16; + + bufptr += buf_pixstride; + dstptr += dst_pixstride; + } + } +} + +const ArtRenderCallback art_render_composite_8_obj = +{ + art_render_composite_8, + art_render_nop_done +}; + + +/* Assumes: + * alpha_buf is NULL + * buf_alpha = ART_ALPHA_NONE (source) + * alpha_type = ART_ALPHA_SEPARATE (dest) + * n_chan = 3; + */ +static void +art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtRenderMaskRun *run = render->run; + int n_run = render->n_run; + int x0 = render->x0; + int x; + int run_x0, run_x1; + art_u8 *image_buf = render->image_buf; + int i, j; + art_u32 tmp; + art_u32 run_alpha; + int image_ix; + art_u8 *bufptr; + art_u32 src_mul; + art_u8 *dstptr; + art_u32 dst_alpha; + art_u32 dst_mul, dst_save_mul; + + image_ix = 0; + for (i = 0; i < n_run - 1; i++) + { + run_x0 = run[i].x; + run_x1 = run[i + 1].x; + tmp = run[i].alpha; + if (tmp < 0x10000) + continue; + + run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ + bufptr = image_buf + (run_x0 - x0) * 3; + dstptr = dest + (run_x0 - x0) * 4; + if (run_alpha == 0x10000) + { + for (x = run_x0; x < run_x1; x++) + { + *dstptr++ = *bufptr++; + *dstptr++ = *bufptr++; + *dstptr++ = *bufptr++; + *dstptr++ = 0xff; + } + } + else + { + for (x = run_x0; x < run_x1; x++) + { + src_mul = run_alpha * 0x101; + + tmp = dstptr[3]; + /* range 0..0xff */ + dst_alpha = (tmp << 8) + tmp + (tmp >> 7); + dst_mul = dst_alpha; + /* dst_alpha is the alpha of the dest pixel, + range 0..0x10000 */ + + dst_mul *= 0x101; + + dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8; + if (dst_alpha == 0) + dst_save_mul = 0xff; + else /* (dst_alpha != 0) */ + dst_save_mul = 0xff0000 / dst_alpha; + + for (j = 0; j < 3; j++) + { + art_u32 src, dst; + art_u32 tmp; + + src = (bufptr[j] * src_mul + 0x8000) >> 16; + dst = (dstptr[j] * dst_mul + 0x8000) >> 16; + tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src; + tmp -= tmp >> 16; + dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; + } + dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16; + + bufptr += 3; + dstptr += 4; + } + } + } +} + + +const ArtRenderCallback art_render_composite_8_opt1_obj = +{ + art_render_composite_8_opt1, + art_render_nop_done +}; + +/* Assumes: + * alpha_buf is NULL + * buf_alpha = ART_ALPHA_PREMUL (source) + * alpha_type = ART_ALPHA_SEPARATE (dest) + * n_chan = 3; + */ +static void +art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtRenderMaskRun *run = render->run; + int n_run = render->n_run; + int x0 = render->x0; + int x; + int run_x0, run_x1; + art_u8 *image_buf = render->image_buf; + int i, j; + art_u32 tmp; + art_u32 run_alpha; + int image_ix; + art_u8 *bufptr; + art_u32 src_alpha; + art_u32 src_mul; + art_u8 *dstptr; + art_u32 dst_alpha; + art_u32 dst_mul, dst_save_mul; + + image_ix = 0; + for (i = 0; i < n_run - 1; i++) + { + run_x0 = run[i].x; + run_x1 = run[i + 1].x; + tmp = run[i].alpha; + if (tmp < 0x10000) + continue; + + run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */ + bufptr = image_buf + (run_x0 - x0) * 4; + dstptr = dest + (run_x0 - x0) * 4; + if (run_alpha == 0x10000) + { + for (x = run_x0; x < run_x1; x++) + { + src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7); + /* src_alpha is the (alpha of the source pixel), + range 0..0x10000 */ + + dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7); + /* dst_alpha is the alpha of the dest pixel, + range 0..0x10000 */ + + dst_mul = dst_alpha*0x101; + + if (src_alpha >= 0x10000) + dst_alpha = 0x10000; + else + dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; + + if (dst_alpha == 0) + dst_save_mul = 0xff; + else /* dst_alpha != 0) */ + dst_save_mul = 0xff0000 / dst_alpha; + + for (j = 0; j < 3; j++) + { + art_u32 src, dst; + art_u32 tmp; + + src = (bufptr[j] << 8) | bufptr[j]; + dst = (dstptr[j] * dst_mul + 0x8000) >> 16; + tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src; + tmp -= tmp >> 16; + dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; + } + dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16; + + bufptr += 4; + dstptr += 4; + } + } + else + { + for (x = run_x0; x < run_x1; x++) + { + tmp = run_alpha * bufptr[3] + 0x80; + /* range 0x80 .. 0xff0080 */ + src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8; + /* src_alpha is the (alpha of the source pixel * alpha), + range 0..0x10000 */ + + src_mul = run_alpha * 0x101; + + tmp = dstptr[3]; + /* range 0..0xff */ + dst_alpha = (tmp << 8) + tmp + (tmp >> 7); + dst_mul = dst_alpha; + /* dst_alpha is the alpha of the dest pixel, + range 0..0x10000 */ + + dst_mul *= 0x101; + + if (src_alpha >= 0x10000) + dst_alpha = 0x10000; + else + dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8; + + if (dst_alpha == 0) + { + dst_save_mul = 0xff; + } + else /* dst_alpha != 0) */ + { + dst_save_mul = 0xff0000 / dst_alpha; + } + + for (j = 0; j < 3; j++) + { + art_u32 src, dst; + art_u32 tmp; + + src = (bufptr[j] * src_mul + 0x8000) >> 16; + dst = (dstptr[j] * dst_mul + 0x8000) >> 16; + tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src; + tmp -= tmp >> 16; + dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16; + } + dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16; + + bufptr += 4; + dstptr += 4; + } + } + } +} + +const ArtRenderCallback art_render_composite_8_opt2_obj = +{ + art_render_composite_8_opt2, + art_render_nop_done +}; + + +/* todo: inline */ +static ArtRenderCallback * +art_render_choose_compositing_callback (ArtRender *render) +{ + if (render->depth == 8 && render->buf_depth == 8) + { + if (render->n_chan == 3 && + render->alpha_buf == NULL && + render->alpha_type == ART_ALPHA_SEPARATE) + { + if (render->buf_alpha == ART_ALPHA_NONE) + return (ArtRenderCallback *)&art_render_composite_8_opt1_obj; + else if (render->buf_alpha == ART_ALPHA_PREMUL) + return (ArtRenderCallback *)&art_render_composite_8_opt2_obj; + } + + return (ArtRenderCallback *)&art_render_composite_8_obj; + } + return (ArtRenderCallback *)&art_render_composite_obj; +} + +/** + * art_render_invoke_callbacks: Invoke the callbacks in the render object. + * @render: The render object. + * @y: The current Y coordinate value. + * + * Invokes the callbacks of the render object in the appropriate + * order. Drivers should call this routine once per scanline. + * + * todo: should management of dest devolve to this routine? very + * plausibly yes. + **/ +void +art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y) +{ + ArtRenderPriv *priv = (ArtRenderPriv *)render; + int i; + + for (i = 0; i < priv->n_callbacks; i++) + { + ArtRenderCallback *callback; + + callback = priv->callbacks[i]; + callback->render (callback, render, dest, y); + } +} + +/** + * art_render_invoke: Perform the requested rendering task. + * @render: The render object. + * + * Invokes the renderer and all sources associated with it, to perform + * the requested rendering task. + **/ +void +art_render_invoke (ArtRender *render) +{ + ArtRenderPriv *priv = (ArtRenderPriv *)render; + int width; + int best_driver, best_score; + int i; + int n_callbacks, n_callbacks_max; + ArtImageSource *image_source; + ArtImageSourceFlags image_flags; + int buf_depth; + ArtAlphaType buf_alpha; + art_boolean first = ART_TRUE; + + if (render == NULL) + { + art_warn ("art_render_invoke: called with render == NULL\n"); + return; + } + if (priv->image_source == NULL) + { + art_warn ("art_render_invoke: no image source given\n"); + return; + } + + width = render->x1 - render->x0; + + render->run = art_new (ArtRenderMaskRun, width + 1); + + /* Elect a mask source as driver. */ + best_driver = -1; + best_score = 0; + for (i = 0; i < priv->n_mask_source; i++) + { + int score; + ArtMaskSource *mask_source; + + mask_source = priv->mask_source[i]; + score = mask_source->can_drive (mask_source, render); + if (score > best_score) + { + best_score = score; + best_driver = i; + } + } + + /* Allocate alpha buffer if needed. */ + if (priv->n_mask_source > 1 || + (priv->n_mask_source == 1 && best_driver < 0)) + { + render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3); + } + + /* Negotiate image rendering and compositing. */ + image_source = priv->image_source; + image_source->negotiate (image_source, render, &image_flags, &buf_depth, + &buf_alpha); + + /* Build callback list. */ + n_callbacks_max = priv->n_mask_source + 3; + priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max); + n_callbacks = 0; + for (i = 0; i < priv->n_mask_source; i++) + if (i != best_driver) + { + ArtMaskSource *mask_source = priv->mask_source[i]; + + mask_source->prepare (mask_source, render, first); + first = ART_FALSE; + priv->callbacks[n_callbacks++] = &mask_source->super; + } + + if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR)) + priv->callbacks[n_callbacks++] = + art_render_choose_clear_callback (render); + + priv->callbacks[n_callbacks++] = &image_source->super; + + /* Allocate image buffer and add compositing callback if needed. */ + if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE)) + { + int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) * + buf_depth) >> 3; + render->buf_depth = buf_depth; + render->buf_alpha = buf_alpha; + render->image_buf = art_new (art_u8, width * bytespp); + priv->callbacks[n_callbacks++] = + art_render_choose_compositing_callback (render); + } + + priv->n_callbacks = n_callbacks; + + if (render->need_span) + render->span_x = art_new (int, width + 1); + + /* Invoke the driver */ + if (best_driver >= 0) + { + ArtMaskSource *driver; + + driver = priv->mask_source[best_driver]; + driver->invoke_driver (driver, render); + } + else + { + art_u8 *dest_ptr = render->pixels; + int y; + + /* Dummy driver */ + render->n_run = 2; + render->run[0].x = render->x0; + render->run[0].alpha = 0x8000 + 0xff * render->opacity; + render->run[1].x = render->x1; + render->run[1].alpha = 0x8000; + if (render->need_span) + { + render->n_span = 2; + render->span_x[0] = render->x0; + render->span_x[1] = render->x1; + } + for (y = render->y0; y < render->y1; y++) + { + art_render_invoke_callbacks (render, dest_ptr, y); + dest_ptr += render->rowstride; + } + } + + if (priv->mask_source != NULL) + art_free (priv->mask_source); + + /* clean up callbacks */ + for (i = 0; i < priv->n_callbacks; i++) + { + ArtRenderCallback *callback; + + callback = priv->callbacks[i]; + callback->done (callback, render); + } + + /* Tear down object */ + if (render->alpha_buf != NULL) + art_free (render->alpha_buf); + if (render->image_buf != NULL) + art_free (render->image_buf); + art_free (render->run); + if (render->span_x != NULL) + art_free (render->span_x); + art_free (priv->callbacks); + art_free (render); +} + +/** + * art_render_mask_solid: Add a solid translucent mask. + * @render: The render object. + * @opacity: Opacity in [0..0x10000] form. + * + * Adds a translucent mask to the rendering object. + **/ +void +art_render_mask_solid (ArtRender *render, int opacity) +{ + art_u32 old_opacity = render->opacity; + art_u32 new_opacity_tmp; + + if (opacity == 0x10000) + /* avoid potential overflow */ + return; + new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000; + render->opacity = new_opacity_tmp >> 16; +} + +/** + * art_render_add_mask_source: Add a mask source to the render object. + * @render: Render object. + * @mask_source: Mask source to add. + * + * This routine adds a mask source to the render object. In general, + * client api's for adding mask sources should just take a render object, + * then the mask source creation function should call this function. + * Clients should never have to call this function directly, unless of + * course they're creating custom mask sources. + **/ +void +art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source) +{ + ArtRenderPriv *priv = (ArtRenderPriv *)render; + int n_mask_source = priv->n_mask_source++; + + if (n_mask_source == 0) + priv->mask_source = art_new (ArtMaskSource *, 1); + /* This predicate is true iff n_mask_source is a power of two */ + else if (!(n_mask_source & (n_mask_source - 1))) + priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *, + n_mask_source << 1); + + priv->mask_source[n_mask_source] = mask_source; +} + +/** + * art_render_add_image_source: Add a mask source to the render object. + * @render: Render object. + * @image_source: Image source to add. + * + * This routine adds an image source to the render object. In general, + * client api's for adding image sources should just take a render + * object, then the mask source creation function should call this + * function. Clients should never have to call this function + * directly, unless of course they're creating custom image sources. + **/ +void +art_render_add_image_source (ArtRender *render, ArtImageSource *image_source) +{ + ArtRenderPriv *priv = (ArtRenderPriv *)render; + + if (priv->image_source != NULL) + { + art_warn ("art_render_add_image_source: image source already present.\n"); + return; + } + priv->image_source = image_source; +} + +/* Solid image source object and methods. Perhaps this should go into a + separate file. */ + +typedef struct _ArtImageSourceSolid ArtImageSourceSolid; + +struct _ArtImageSourceSolid { + ArtImageSource super; + ArtPixMaxDepth color[ART_MAX_CHAN]; + art_u32 *rgbtab; + art_boolean init; +}; + +static void +art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render) +{ + ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; + + if (z->rgbtab != NULL) + art_free (z->rgbtab); + art_free (self); +} + +static void +art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render) +{ + ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; + ArtPixMaxDepth color_max; + int r_fg, g_fg, b_fg; + int r_bg, g_bg, b_bg; + int r, g, b; + int dr, dg, db; + int i; + int tmp; + art_u32 *rgbtab; + + rgbtab = art_new (art_u32, 256); + z->rgbtab = rgbtab; + + color_max = self->color[0]; + r_fg = ART_PIX_8_FROM_MAX (color_max); + color_max = self->color[1]; + g_fg = ART_PIX_8_FROM_MAX (color_max); + color_max = self->color[2]; + b_fg = ART_PIX_8_FROM_MAX (color_max); + + color_max = render->clear_color[0]; + r_bg = ART_PIX_8_FROM_MAX (color_max); + color_max = render->clear_color[1]; + g_bg = ART_PIX_8_FROM_MAX (color_max); + color_max = render->clear_color[2]; + b_bg = ART_PIX_8_FROM_MAX (color_max); + + r = (r_bg << 16) + 0x8000; + g = (g_bg << 16) + 0x8000; + b = (b_bg << 16) + 0x8000; + tmp = ((r_fg - r_bg) << 16) + 0x80; + dr = (tmp + (tmp >> 8)) >> 8; + tmp = ((g_fg - g_bg) << 16) + 0x80; + dg = (tmp + (tmp >> 8)) >> 8; + tmp = ((b_fg - b_bg) << 16) + 0x80; + db = (tmp + (tmp >> 8)) >> 8; + + for (i = 0; i < 256; i++) + { + rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16); + r += dr; + g += dg; + b += db; + } +} + +static void +art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; + ArtRenderMaskRun *run = render->run; + int n_run = render->n_run; + art_u32 *rgbtab = z->rgbtab; + art_u32 rgb; + int x0 = render->x0; + int x1 = render->x1; + int run_x0, run_x1; + int i; + int ix; + + if (n_run > 0) + { + run_x1 = run[0].x; + if (run_x1 > x0) + { + rgb = rgbtab[0]; + art_rgb_fill_run (dest, + rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff, + run_x1 - x0); + } + for (i = 0; i < n_run - 1; i++) + { + run_x0 = run_x1; + run_x1 = run[i + 1].x; + rgb = rgbtab[(run[i].alpha >> 16) & 0xff]; + ix = (run_x0 - x0) * 3; +#define OPTIMIZE_LEN_1 +#ifdef OPTIMIZE_LEN_1 + if (run_x1 - run_x0 == 1) + { + dest[ix] = rgb >> 16; + dest[ix + 1] = (rgb >> 8) & 0xff; + dest[ix + 2] = rgb & 0xff; + } + else + { + art_rgb_fill_run (dest + ix, + rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff, + run_x1 - run_x0); + } +#else + art_rgb_fill_run (dest + ix, + rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff, + run_x1 - run_x0); +#endif + } + } + else + { + run_x1 = x0; + } + if (run_x1 < x1) + { + rgb = rgbtab[0]; + art_rgb_fill_run (dest + (run_x1 - x0) * 3, + rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff, + x1 - run_x1); + } +} + +static void +art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y) +{ + ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; + int width = render->x1 - render->x0; + art_u8 r, g, b; + ArtPixMaxDepth color_max; + + /* todo: replace this simple test with real sparseness */ + if (z->init) + return; + z->init = ART_TRUE; + + color_max = z->color[0]; + r = ART_PIX_8_FROM_MAX (color_max); + color_max = z->color[1]; + g = ART_PIX_8_FROM_MAX (color_max); + color_max = z->color[2]; + b = ART_PIX_8_FROM_MAX (color_max); + + art_rgb_fill_run (render->image_buf, r, g, b, width); +} + +static void +art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render, + ArtImageSourceFlags *p_flags, + int *p_buf_depth, ArtAlphaType *p_alpha) +{ + ArtImageSourceSolid *z = (ArtImageSourceSolid *)self; + ArtImageSourceFlags flags = 0; + static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render, + art_u8 *dest, int y); + + render_cbk = NULL; + + if (render->depth == 8 && render->n_chan == 3 && + render->alpha_type == ART_ALPHA_NONE) + { + if (render->clear) + { + render_cbk = art_render_image_solid_rgb8_opaq; + flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE; + art_render_image_solid_rgb8_opaq_init (z, render); + } + } + if (render_cbk == NULL) + { + if (render->depth == 8) + { + render_cbk = art_render_image_solid_rgb8; + *p_buf_depth = 8; + *p_alpha = ART_ALPHA_NONE; /* todo */ + } + } + /* todo: general case */ + self->super.render = render_cbk; + *p_flags = flags; +} + +/** + * art_render_image_solid: Add a solid color image source. + * @render: The render object. + * @color: Color. + * + * Adds an image source with the solid color given by @color. The + * color need not be retained in memory after this call. + **/ +void +art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color) +{ + ArtImageSourceSolid *image_source; + int i; + + image_source = art_new (ArtImageSourceSolid, 1); + image_source->super.super.render = NULL; + image_source->super.super.done = art_render_image_solid_done; + image_source->super.negotiate = art_render_image_solid_negotiate; + + for (i = 0; i < render->n_chan; i++) + image_source->color[i] = color[i]; + + image_source->rgbtab = NULL; + image_source->init = ART_FALSE; + + art_render_add_image_source (render, &image_source->super); +}