diff options
Diffstat (limited to 'libtwin/twin_draw.c')
-rw-r--r-- | libtwin/twin_draw.c | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/libtwin/twin_draw.c b/libtwin/twin_draw.c new file mode 100644 index 0000000..48a9da6 --- /dev/null +++ b/libtwin/twin_draw.c @@ -0,0 +1,696 @@ +/* + * Twin - A Tiny Window System + * Copyright © 2004 Keith Packard <keithp@keithp.com> + * All rights reserved. + * + * 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 the Twin Library; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "twinint.h" + +/* op, src, dst */ +static twin_src_op comp2[2][4][3] = { + { /* OVER */ + { /* A8 */ + _twin_a8_over_a8, + _twin_a8_over_rgb16, + _twin_a8_over_argb32, + }, + { /* RGB16 */ + _twin_rgb16_over_a8, + _twin_rgb16_over_rgb16, + _twin_rgb16_over_argb32, + }, + { /* ARGB32 */ + _twin_argb32_over_a8, + _twin_argb32_over_rgb16, + _twin_argb32_over_argb32, + }, + { /* C */ + _twin_c_over_a8, + _twin_c_over_rgb16, + _twin_c_over_argb32, + } + }, + { /* SOURCE */ + { /* A8 */ + _twin_a8_source_a8, + _twin_a8_source_rgb16, + _twin_a8_source_argb32, + }, + { /* RGB16 */ + _twin_rgb16_source_a8, + _twin_rgb16_source_rgb16, + _twin_rgb16_source_argb32, + }, + { /* ARGB32 */ + _twin_argb32_source_a8, + _twin_argb32_source_rgb16, + _twin_argb32_source_argb32, + }, + { /* C */ + _twin_c_source_a8, + _twin_c_source_rgb16, + _twin_c_source_argb32, + } + } +}; + +/* op, src, msk, dst */ +static twin_src_msk_op comp3[2][4][4][3] = { + { /* OVER */ + { /* A8 */ + { /* A8 */ + _twin_a8_in_a8_over_a8, + _twin_a8_in_a8_over_rgb16, + _twin_a8_in_a8_over_argb32, + }, + { /* RGB16 */ + _twin_a8_in_rgb16_over_a8, + _twin_a8_in_rgb16_over_rgb16, + _twin_a8_in_rgb16_over_argb32, + }, + { /* ARGB32 */ + _twin_a8_in_argb32_over_a8, + _twin_a8_in_argb32_over_rgb16, + _twin_a8_in_argb32_over_argb32, + }, + { /* C */ + _twin_a8_in_c_over_a8, + _twin_a8_in_c_over_rgb16, + _twin_a8_in_c_over_argb32, + }, + }, + { /* RGB16 */ + { /* A8 */ + _twin_rgb16_in_a8_over_a8, + _twin_rgb16_in_a8_over_rgb16, + _twin_rgb16_in_a8_over_argb32, + }, + { /* RGB16 */ + _twin_rgb16_in_rgb16_over_a8, + _twin_rgb16_in_rgb16_over_rgb16, + _twin_rgb16_in_rgb16_over_argb32, + }, + { /* ARGB32 */ + _twin_rgb16_in_argb32_over_a8, + _twin_rgb16_in_argb32_over_rgb16, + _twin_rgb16_in_argb32_over_argb32, + }, + { /* C */ + _twin_rgb16_in_c_over_a8, + _twin_rgb16_in_c_over_rgb16, + _twin_rgb16_in_c_over_argb32, + }, + }, + { /* ARGB32 */ + { /* A8 */ + _twin_argb32_in_a8_over_a8, + _twin_argb32_in_a8_over_rgb16, + _twin_argb32_in_a8_over_argb32, + }, + { /* RGB16 */ + _twin_argb32_in_rgb16_over_a8, + _twin_argb32_in_rgb16_over_rgb16, + _twin_argb32_in_rgb16_over_argb32, + }, + { /* ARGB32 */ + _twin_argb32_in_argb32_over_a8, + _twin_argb32_in_argb32_over_rgb16, + _twin_argb32_in_argb32_over_argb32, + }, + { /* C */ + _twin_argb32_in_c_over_a8, + _twin_argb32_in_c_over_rgb16, + _twin_argb32_in_c_over_argb32, + }, + }, + { /* C */ + { /* A8 */ + _twin_c_in_a8_over_a8, + _twin_c_in_a8_over_rgb16, + _twin_c_in_a8_over_argb32, + }, + { /* RGB16 */ + _twin_c_in_rgb16_over_a8, + _twin_c_in_rgb16_over_rgb16, + _twin_c_in_rgb16_over_argb32, + }, + { /* ARGB32 */ + _twin_c_in_argb32_over_a8, + _twin_c_in_argb32_over_rgb16, + _twin_c_in_argb32_over_argb32, + }, + { /* C */ + _twin_c_in_c_over_a8, + _twin_c_in_c_over_rgb16, + _twin_c_in_c_over_argb32, + }, + }, + }, + { /* SOURCE */ + { /* A8 */ + { /* A8 */ + _twin_a8_in_a8_source_a8, + _twin_a8_in_a8_source_rgb16, + _twin_a8_in_a8_source_argb32, + }, + { /* RGB16 */ + _twin_a8_in_rgb16_source_a8, + _twin_a8_in_rgb16_source_rgb16, + _twin_a8_in_rgb16_source_argb32, + }, + { /* ARGB32 */ + _twin_a8_in_argb32_source_a8, + _twin_a8_in_argb32_source_rgb16, + _twin_a8_in_argb32_source_argb32, + }, + { /* C */ + _twin_a8_in_c_source_a8, + _twin_a8_in_c_source_rgb16, + _twin_a8_in_c_source_argb32, + }, + }, + { /* RGB16 */ + { /* A8 */ + _twin_rgb16_in_a8_source_a8, + _twin_rgb16_in_a8_source_rgb16, + _twin_rgb16_in_a8_source_argb32, + }, + { /* RGB16 */ + _twin_rgb16_in_rgb16_source_a8, + _twin_rgb16_in_rgb16_source_rgb16, + _twin_rgb16_in_rgb16_source_argb32, + }, + { /* ARGB32 */ + _twin_rgb16_in_argb32_source_a8, + _twin_rgb16_in_argb32_source_rgb16, + _twin_rgb16_in_argb32_source_argb32, + }, + { /* C */ + _twin_rgb16_in_c_source_a8, + _twin_rgb16_in_c_source_rgb16, + _twin_rgb16_in_c_source_argb32, + }, + }, + { /* ARGB32 */ + { /* A8 */ + _twin_argb32_in_a8_source_a8, + _twin_argb32_in_a8_source_rgb16, + _twin_argb32_in_a8_source_argb32, + }, + { /* RGB16 */ + _twin_argb32_in_rgb16_source_a8, + _twin_argb32_in_rgb16_source_rgb16, + _twin_argb32_in_rgb16_source_argb32, + }, + { /* ARGB32 */ + _twin_argb32_in_argb32_source_a8, + _twin_argb32_in_argb32_source_rgb16, + _twin_argb32_in_argb32_source_argb32, + }, + { /* C */ + _twin_argb32_in_c_source_a8, + _twin_argb32_in_c_source_rgb16, + _twin_argb32_in_c_source_argb32, + }, + }, + { /* C */ + { /* A8 */ + _twin_c_in_a8_source_a8, + _twin_c_in_a8_source_rgb16, + _twin_c_in_a8_source_argb32, + }, + { /* RGB16 */ + _twin_c_in_rgb16_source_a8, + _twin_c_in_rgb16_source_rgb16, + _twin_c_in_rgb16_source_argb32, + }, + { /* ARGB32 */ + _twin_c_in_argb32_source_a8, + _twin_c_in_argb32_source_rgb16, + _twin_c_in_argb32_source_argb32, + }, + { /* C */ + _twin_c_in_c_source_a8, + _twin_c_in_c_source_rgb16, + _twin_c_in_c_source_argb32, + }, + }, + } +}; + + +#define operand_index(o) ((o)->source_kind == TWIN_SOLID ? 3 : o->u.pixmap->format) + +/* XXX Fixme: source clipping is busted + */ +static void _twin_composite_simple (twin_pixmap_t *dst, + twin_coord_t dst_x, + twin_coord_t dst_y, + twin_operand_t *src, + twin_coord_t src_x, + twin_coord_t src_y, + twin_operand_t *msk, + twin_coord_t msk_x, + twin_coord_t msk_y, + twin_operator_t operator, + twin_coord_t width, + twin_coord_t height) +{ + twin_coord_t iy; + twin_coord_t left, top, right, bottom; + twin_coord_t sdx, sdy; + twin_source_u s; + + dst_x += dst->origin_x; + dst_y += dst->origin_y; + left = dst_x; + top = dst_y; + right = dst_x + width; + bottom = dst_y + height; + + /* clip */ + if (left < dst->clip.left) + left = dst->clip.left; + if (top < dst->clip.top) + top = dst->clip.top; + if (right > dst->clip.right) + right = dst->clip.right; + if (bottom > dst->clip.bottom) + bottom = dst->clip.bottom; + + if (left >= right || top >= bottom) + return; + + if (src->source_kind == TWIN_PIXMAP) { + src_x += src->u.pixmap->origin_x; + src_y += src->u.pixmap->origin_y; + } else + s.c = src->u.argb; + + sdx = src_x - dst_x; + sdy = src_y - dst_y; + + if (msk) { + twin_src_msk_op op; + twin_source_u m; + twin_coord_t mdx, mdy; + + if (msk->source_kind == TWIN_PIXMAP) { + msk_x += msk->u.pixmap->origin_x; + msk_y += msk->u.pixmap->origin_y; + } else + m.c = msk->u.argb; + + mdx = msk_x - dst_x; + mdy = msk_y - dst_y; + + op = comp3[operator][operand_index(src)][operand_index(msk)][dst->format]; + for (iy = top; iy < bottom; iy++) + { + if (src->source_kind == TWIN_PIXMAP) + s.p = twin_pixmap_pointer (src->u.pixmap, left+sdx, iy+sdy); + if (msk->source_kind == TWIN_PIXMAP) + m.p = twin_pixmap_pointer (msk->u.pixmap, left+mdx, iy+mdy); + (*op) (twin_pixmap_pointer (dst, left, iy), s, m, right - left); + } + } + else + { + twin_src_op op; + + op = comp2[operator][operand_index(src)][dst->format]; + + for (iy = top; iy < bottom; iy++) + { + if (src->source_kind == TWIN_PIXMAP) + s.p = twin_pixmap_pointer (src->u.pixmap, left+sdx, iy+sdy); + (*op) (twin_pixmap_pointer (dst, left, iy), s, right - left); + } + } + twin_pixmap_damage (dst, left, top, right, bottom); +} + +static inline int operand_xindex(twin_operand_t *o) +{ + int ind = operand_index(o); + + return ind != TWIN_RGB16 ? ind : TWIN_ARGB32; +} + +static twin_xform_t *twin_pixmap_init_xform (twin_pixmap_t *pixmap, + twin_coord_t left, twin_coord_t width, + twin_coord_t src_x, twin_coord_t src_y) +{ + twin_xform_t *xform; + twin_format_t fmt = pixmap->format; + + if (fmt == TWIN_RGB16) + fmt = TWIN_ARGB32; + + xform = calloc(1, sizeof (twin_xform_t) + + width * twin_bytes_per_pixel(fmt)); + if (xform == NULL) + return NULL; + + xform->span.v = (twin_argb32_t *)(char *)(xform + 1); + xform->pixmap = pixmap; + xform->left = left; + xform->width = width; + xform->src_x = src_x; + xform->src_y = src_y; + + return xform; +} + +static void twin_pixmap_free_xform (twin_xform_t *xform) +{ + if (xform) + free (xform); +} + +#define FX(x) twin_int_to_fixed(x) +#define XF(x) twin_fixed_to_int(x) + +/* we are doing clipping on source... dunno if that makes much sense + * but here we go ... if we decide that source clipping makes no sense + * then we need to still test wether we fit in the pixmap boundaries + * here. source clipping is useful if you try to extract one image + * out of a big picture though. + */ +#define _pix_clipped(pix, x, y) \ + ((x) < FX((pix)->clip.left) || (x) >= FX((pix)->clip.right) || \ + (y) < FX((pix)->clip.top) || (y) >= FX((pix)->clip.bottom)) + +#define _get_pix_8(d, pix, x, y) \ + do { \ + (d) = _pix_clipped(pix, x, y) ? 0 : \ + *((pix)->p.a8 + XF(y) * (pix)->stride + XF(x)); \ + } while(0) + +#define _get_pix_16(d, pix, x, y) \ + do { \ + twin_rgb16_t p = _pix_clipped(pix, x, y) ? 0 : \ + *((pix)->p.argb32 + XF(y) * ((pix)->stride >> 1) + XF(x)); \ + *((twin_argb32_t *)(char *)(d)) = twin_rgb16_to_argb32(p); \ + } while(0) + +#define _get_pix_32(d, pix, x, y) \ + do { \ + twin_argb32_t p = _pix_clipped(pix, x, y) ? 0 : \ + *((pix)->p.argb32 + XF(y) * ((pix)->stride >> 2) + XF(x)); \ + *((twin_argb32_t *)(char *)(d)) = p; \ + } while(0) + + +#define _pix_saucemix(tl, tr, bl, br, wx, wy) \ + (( ((((br * wx) + (bl * (TWIN_FIXED_ONE - wx))) >> 16) \ + * wy) + \ + ((((tr * wx) + (tl * (TWIN_FIXED_ONE - wx))) >> 16) \ + * (TWIN_FIXED_ONE - wy)) \ + ) >> 16) + + +static void twin_pixmap_read_xform_8 (twin_xform_t *xform, twin_coord_t line) +{ + twin_fixed_t dx, dy, sx, sy; + unsigned int wx, wy; + twin_a8_t pts[4]; + twin_a8_t *dst = xform->span.a8; + twin_pixmap_t *pix = xform->pixmap; + twin_matrix_t *tfm = &xform->pixmap->transform; + + /* for each row in the dest line */ + dy = twin_int_to_fixed(line); + for (dx = 0; dx < twin_int_to_fixed(xform->width); dx += TWIN_FIXED_ONE) { + sx = _twin_matrix_fx(tfm, dx, dy) + FX(xform->src_x); + sy = _twin_matrix_fy(tfm, dx, dy) + FX(xform->src_y); + _get_pix_8(pts[0], pix, sx, sy); + _get_pix_8(pts[1], pix, sx + TWIN_FIXED_ONE, sy); + _get_pix_8(pts[2], pix, sx, sy + TWIN_FIXED_ONE); + _get_pix_8(pts[3], pix, sx, sy + TWIN_FIXED_ONE); + wx = sx & 0xffff; + wy = sy & 0xffff; + *(dst++) = _pix_saucemix(pts[0], pts[1], pts[2], pts[4], wx, wy); + } +} + +static void twin_pixmap_read_xform_16 (twin_xform_t *xform, twin_coord_t line) +{ + twin_fixed_t dx, dy, sx, sy; + unsigned int wx, wy; + twin_a8_t pts[4][4]; + twin_a8_t *dst = xform->span.a8; + twin_pixmap_t *pix = xform->pixmap; + twin_matrix_t *tfm = &xform->pixmap->transform; + + /* for each row in the dest line */ + dy = twin_int_to_fixed(line); + for (dx = 0; dx < twin_int_to_fixed(xform->width); dx += TWIN_FIXED_ONE) { + sx = _twin_matrix_fx(tfm, dx, dy) + FX(xform->src_x); + sy = _twin_matrix_fy(tfm, dx, dy) + FX(xform->src_y); + _get_pix_16(pts[0], pix, sx, sy); + _get_pix_16(pts[1], pix, sx + TWIN_FIXED_ONE, sy); + _get_pix_16(pts[2], pix, sx, sy + TWIN_FIXED_ONE); + _get_pix_16(pts[3], pix, sx + TWIN_FIXED_ONE, sy + TWIN_FIXED_ONE); + wx = sx & 0xffff; + wy = sy & 0xffff; + *(dst++) = _pix_saucemix(pts[0][0], pts[1][0], + pts[2][0], pts[3][0], wx, wy); + *(dst++) = _pix_saucemix(pts[0][1], pts[1][1], + pts[2][1], pts[3][1], wx, wy); + *(dst++) = _pix_saucemix(pts[0][2], pts[1][2], + pts[2][2], pts[3][2], wx, wy); + *(dst++) = _pix_saucemix(pts[0][3], pts[1][3], + pts[2][3], pts[3][3], wx, wy); + } +} + +static void twin_pixmap_read_xform_32 (twin_xform_t *xform, twin_coord_t line) +{ + twin_fixed_t dx, dy, sx, sy; + unsigned int wx, wy; + twin_a8_t pts[4][4]; + twin_a8_t *dst = xform->span.a8; + twin_pixmap_t *pix = xform->pixmap; + twin_matrix_t *tfm = &xform->pixmap->transform; + + /* for each row in the dest line */ + dy = twin_int_to_fixed(line); + for (dx = 0; dx < twin_int_to_fixed(xform->width); dx += TWIN_FIXED_ONE) { + sx = _twin_matrix_fx(tfm, dx, dy) + FX(xform->src_x); + sy = _twin_matrix_fy(tfm, dx, dy) + FX(xform->src_y); + _get_pix_32(pts[0], pix, sx, sy); + _get_pix_32(pts[1], pix, sx + TWIN_FIXED_ONE, sy); + _get_pix_32(pts[2], pix, sx, sy + TWIN_FIXED_ONE); + _get_pix_32(pts[3], pix, sx + TWIN_FIXED_ONE, sy + TWIN_FIXED_ONE); + wx = sx & 0xffff; + wy = sy & 0xffff; + *(dst++) = _pix_saucemix(pts[0][0], pts[1][0], + pts[2][0], pts[3][0], wx, wy); + *(dst++) = _pix_saucemix(pts[0][1], pts[1][1], + pts[2][1], pts[3][1], wx, wy); + *(dst++) = _pix_saucemix(pts[0][2], pts[1][2], + pts[2][2], pts[3][2], wx, wy); + *(dst++) = _pix_saucemix(pts[0][3], pts[1][3], + pts[2][3], pts[3][3], wx, wy); + } +} + +static void twin_pixmap_read_xform (twin_xform_t *xform, twin_coord_t line) +{ + if (xform->pixmap->format == TWIN_A8) + twin_pixmap_read_xform_8(xform, line); + else if (xform->pixmap->format == TWIN_RGB16) + twin_pixmap_read_xform_16(xform, line); + else if (xform->pixmap->format == TWIN_ARGB32) + twin_pixmap_read_xform_32(xform, line); +} + +static void _twin_composite_xform (twin_pixmap_t *dst, + twin_coord_t dst_x, + twin_coord_t dst_y, + twin_operand_t *src, + twin_coord_t src_x, + twin_coord_t src_y, + twin_operand_t *msk, + twin_coord_t msk_x, + twin_coord_t msk_y, + twin_operator_t operator, + twin_coord_t width, + twin_coord_t height) +{ + twin_coord_t iy; + twin_coord_t left, top, right, bottom; + twin_xform_t *sxform = NULL, *mxform = NULL; + twin_source_u s; + + dst_x += dst->origin_x; + dst_y += dst->origin_y; + left = dst_x; + top = dst_y; + right = dst_x + width; + bottom = dst_y + height; + + /* clip */ + if (left < dst->clip.left) + left = dst->clip.left; + if (top < dst->clip.top) + top = dst->clip.top; + if (right > dst->clip.right) + right = dst->clip.right; + if (bottom > dst->clip.bottom) + bottom = dst->clip.bottom; + + if (left >= right || top >= bottom) + return; + + width = right - left; + height = bottom - top; + + if (src->source_kind == TWIN_PIXMAP) { + src_x += src->u.pixmap->origin_x; + src_y += src->u.pixmap->origin_y; + sxform = twin_pixmap_init_xform(src->u.pixmap, left, width, + src_x, src_y); + if (sxform == NULL) + return; + s.p = sxform->span; + } else + s.c = src->u.argb; + + if (msk) { + twin_src_msk_op op; + twin_source_u m; + + if (msk->source_kind == TWIN_PIXMAP) { + msk_x += msk->u.pixmap->origin_x; + msk_y += msk->u.pixmap->origin_y; + mxform = twin_pixmap_init_xform(msk->u.pixmap, left, width, + msk_x, msk_y); + if (mxform == NULL) + return; + m.p = mxform->span; + } else + m.c = msk->u.argb; + + op = comp3[operator][operand_xindex(src)][operand_xindex(msk)] + [dst->format]; + for (iy = top; iy < bottom; iy++) { + if (src->source_kind == TWIN_PIXMAP) + twin_pixmap_read_xform (sxform, iy - top); + if (msk->source_kind == TWIN_PIXMAP) + twin_pixmap_read_xform (mxform, iy - top); + (*op) (twin_pixmap_pointer (dst, left, iy), s, m, right - left); + } + } else { + twin_src_op op; + + op = comp2[operator][operand_xindex(src)][dst->format]; + + for (iy = top; iy < bottom; iy++) { + if (src->source_kind == TWIN_PIXMAP) + twin_pixmap_read_xform (sxform, iy - top); + (*op) (twin_pixmap_pointer (dst, left, iy), s, right - left); + } + } + twin_pixmap_damage (dst, left, top, right, bottom); + twin_pixmap_free_xform(sxform); + twin_pixmap_free_xform(mxform); +} + +void twin_composite (twin_pixmap_t *dst, + twin_coord_t dst_x, + twin_coord_t dst_y, + twin_operand_t *src, + twin_coord_t src_x, + twin_coord_t src_y, + twin_operand_t *msk, + twin_coord_t msk_x, + twin_coord_t msk_y, + twin_operator_t operator, + twin_coord_t width, + twin_coord_t height) +{ + if ((src->source_kind == TWIN_PIXMAP && + !twin_matrix_is_identity(&src->u.pixmap->transform)) || + (msk && (msk->source_kind == TWIN_PIXMAP && + !twin_matrix_is_identity(&msk->u.pixmap->transform)))) + _twin_composite_xform(dst, dst_x, dst_y, + src, src_x, src_y, + msk, msk_x, msk_y, + operator, width, height); + else + _twin_composite_simple(dst, dst_x, dst_y, + src, src_x, src_y, + msk, msk_x, msk_y, + operator, width, height); +} + +/* + * array primary index is OVER SOURCE + * array secondary index is ARGB32 RGB16 A8 + */ +static twin_src_op fill[2][3] = { + { /* OVER */ + _twin_c_over_a8, + _twin_c_over_rgb16, + _twin_c_over_argb32, + }, + { /* SOURCE */ + _twin_c_source_a8, + _twin_c_source_rgb16, + _twin_c_source_argb32, + } +}; + +void +twin_fill (twin_pixmap_t *dst, + twin_argb32_t pixel, + twin_operator_t operator, + twin_coord_t left, + twin_coord_t top, + twin_coord_t right, + twin_coord_t bottom) +{ + twin_src_op op; + twin_source_u src; + twin_coord_t iy; + + /* offset */ + left += dst->origin_x; + top += dst->origin_y; + right += dst->origin_x; + bottom += dst->origin_y; + + /* clip */ + if (left < dst->clip.left) + left = dst->clip.left; + if (right > dst->clip.right) + right = dst->clip.right; + if (top < dst->clip.top) + top = dst->clip.top; + if (bottom > dst->clip.bottom) + bottom = dst->clip.bottom; + if (left >= right || top >= bottom) + return; + src.c = pixel; + op = fill[operator][dst->format]; + for (iy = top; iy < bottom; iy++) + (*op) (twin_pixmap_pointer (dst, left, iy), src, right - left); + twin_pixmap_damage (dst, left, top, right, bottom); +} |