/* * 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; gint n_mask_source; ArtMaskSource **mask_source; gint n_callbacks; ArtRenderCallback **callbacks; }; static void art_render_nop_done (ArtRenderCallback *self, ArtRender *render) { } static void art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render, art_u8 *dest, gint y) { gint 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, gint y) { gint width = render->x1 - render->x0; gint i, j; gint n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); gint 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, gint y) { gint width = render->x1 - render->x0; gint i, j; gint n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE); gint ix; art_u16 *dest_16 = (art_u16 *)dest; art_u8 color[ART_MAX_CHAN + 1]; for (j = 0; j < n_ch; j++) { gint 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 */ /* 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, gint y) { ArtRenderMaskRun *run = render->run; art_u32 depth = render->depth; gint n_run = render->n_run; gint x0 = render->x0; gint x; gint run_x0, run_x1; art_u8 *alpha_buf = render->alpha_buf; art_u8 *image_buf = render->image_buf; gint i, j; art_u32 tmp; art_u32 run_alpha; art_u32 alpha; gint image_ix; art_u16 src[ART_MAX_CHAN + 1]; art_u16 dst[ART_MAX_CHAN + 1]; gint n_chan = render->n_chan; ArtAlphaType alpha_type = render->alpha_type; gint n_ch = n_chan + (alpha_type != ART_ALPHA_NONE); gint dst_pixstride = n_ch * (depth >> 3); gint buf_depth = render->buf_depth; ArtAlphaType buf_alpha = render->buf_alpha; gint buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE); gint 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; 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, gint y) { ArtRenderMaskRun *run = render->run; gint n_run = render->n_run; gint x0 = render->x0; gint x; gint run_x0, run_x1; art_u8 *alpha_buf = render->alpha_buf; art_u8 *image_buf = render->image_buf; gint i, j; art_u32 tmp; art_u32 run_alpha; art_u32 alpha; gint image_ix; gint n_chan = render->n_chan; ArtAlphaType alpha_type = render->alpha_type; gint n_ch = n_chan + (alpha_type != ART_ALPHA_NONE); gint dst_pixstride = n_ch; ArtAlphaType buf_alpha = render->buf_alpha; gint buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE); gint 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; 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, gint y) { ArtRenderMaskRun *run = render->run; gint n_run = render->n_run; gint x0 = render->x0; gint x; gint run_x0, run_x1; art_u8 *image_buf = render->image_buf; gint i, j; art_u32 tmp; art_u32 run_alpha; gint 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; 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, gint y) { ArtRenderMaskRun *run = render->run; gint n_run = render->n_run; gint x0 = render->x0; gint x; gint run_x0, run_x1; art_u8 *image_buf = render->image_buf; gint i, j; art_u32 tmp; art_u32 run_alpha; gint 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; 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 };