diff options
author | Benjamin Herrenschmidt benh@kernel.crashing.org <benh@tika.localdomain> | 2006-11-28 19:43:50 +1100 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@tika.localdomain> | 2006-11-28 19:43:50 +1100 |
commit | a9ae3dc5ea52621f7092a60168c5da794891847b (patch) | |
tree | 52357bb5840b60bcb13b615a197fc6e10c5b592f | |
parent | 56d2a170bcddf2906d7ce6b5ba27a14c44947013 (diff) | |
download | libtwin-a9ae3dc5ea52621f7092a60168c5da794891847b.tar.gz |
Add some basic affine transform support to compositing.
The transform matrix is currently stored in the pixmap, though
it is only ever used when that pixmap is a source of a composite
operation (not for windows refresh, damage, etc...) thus I might
move it to twin_operand_t.
The implementation is totally sub-optimal but it gives a working
basis. As Keith suggested, we could use a bresenham-type algorithm
in the transform loop to avoiding having to transform every pixel
through the matrix. That will probably significantly improve
performances. There are also minor optimisations that can be done
in a few corners.
Also the matrix is used as a reverse transform, which can
be confusing. I'll fix it one of these days to operate on an
inverted matrix instead.
While playing with it, I noticed several issues with the existing
non-transform composite function. A bug I fixed related to masks,
but also the source clipping seems to be busted.
It adds the clip top/left to the src_x/y & msk_x/y which doesn't
make much sense to me and doesn't clip when fetching pixels, thus
forcing you to have perfectly adjusted width and height args to
twin_composite() that match the source image size (which stops
making sense when using transforms). I haven't fixed it yet but
I will do so, possibly by merging xform and simple into one
function (thus always using a temporary span buffer) or by adding
a bit of clipping code to the simple function's inner loop.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | twin.h | 22 | ||||
-rw-r--r-- | twin_draw.c | 345 | ||||
-rw-r--r-- | twin_matrix.c | 24 | ||||
-rw-r--r-- | twin_pixmap.c | 3 | ||||
-rw-r--r-- | twinint.h | 16 |
5 files changed, 369 insertions, 41 deletions
@@ -39,6 +39,7 @@ typedef int16_t twin_keysym_t; typedef int32_t twin_area_t; typedef int32_t twin_time_t; typedef int16_t twin_stretch_t; +typedef int32_t twin_fixed_t; /* 16.16 format */ #define TWIN_FALSE 0 #define TWIN_TRUE 1 @@ -71,6 +72,13 @@ typedef struct _twin_rect { twin_coord_t left, right, top, bottom; } twin_rect_t; +/* + * Place matrices in structures so they can be easily copied + */ +typedef struct _twin_matrix { + twin_fixed_t m[3][2]; +} twin_matrix_t; + typedef union _twin_pointer { void *v; uint8_t *b; @@ -105,6 +113,8 @@ typedef struct _twin_pixmap { twin_coord_t width; /* pixels */ twin_coord_t height; /* pixels */ twin_coord_t stride; /* bytes */ + twin_matrix_t transform; + /* * Clipping - a single rectangle in pixmap coordinates. * Drawing is done relative to this rectangle @@ -204,8 +214,6 @@ typedef struct _twin_operand { typedef enum { TWIN_OVER, TWIN_SOURCE } twin_operator_t; -typedef int32_t twin_fixed_t; /* 16.16 format */ - #define TWIN_FIXED_ONE (0x10000) #define TWIN_FIXED_HALF (0x08000) #define TWIN_FIXED_MAX (0x7fffffff) @@ -227,13 +235,6 @@ typedef struct _twin_point { twin_fixed_t x, y; } twin_point_t; -/* - * Place matrices in structures so they can be easily copied - */ -typedef struct _twin_matrix { - twin_fixed_t m[3][2]; -} twin_matrix_t; - typedef struct _twin_path twin_path_t; typedef enum _twin_cap { @@ -726,6 +727,9 @@ twin_label_set (twin_label_t *label, void twin_matrix_identity (twin_matrix_t *m); +twin_bool_t +twin_matrix_is_identity (twin_matrix_t *m); + void twin_matrix_translate (twin_matrix_t *m, twin_fixed_t tx, twin_fixed_t ty); diff --git a/twin_draw.c b/twin_draw.c index b6df9ba..f8e88dd 100644 --- a/twin_draw.c +++ b/twin_draw.c @@ -256,19 +256,20 @@ static twin_src_msk_op comp3[2][4][4][3] = { #define operand_index(o) ((o)->source_kind == TWIN_SOLID ? 3 : o->u.pixmap->format) -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) +/* 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; @@ -281,6 +282,7 @@ twin_composite (twin_pixmap_t *dst, top = dst_y; right = dst_x + width; bottom = dst_y + height; + /* clip */ if (left < dst->clip.left) left = dst->clip.left; @@ -294,30 +296,19 @@ twin_composite (twin_pixmap_t *dst, if (left >= right || top >= bottom) return; - if (src->source_kind == TWIN_PIXMAP) - { - src_x += src->u.pixmap->clip.left; - src_y += src->u.pixmap->clip.top; - } - else + if (src->source_kind != TWIN_PIXMAP) s.c = src->u.argb; sdx = src_x - dst_x; sdy = src_y - dst_y; - if (msk) - { + 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->clip.left; - msk_y += msk->u.pixmap->clip.top; - } - else - s.c = msk->u.argb; + if (msk->source_kind != TWIN_PIXMAP) + m.c = msk->u.argb; mdx = msk_x - dst_x; mdy = msk_y - dst_y; @@ -329,8 +320,7 @@ twin_composite (twin_pixmap_t *dst, 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); + (*op) (twin_pixmap_pointer (dst, left, iy), s, m, right - left); } } else @@ -343,13 +333,304 @@ twin_composite (twin_pixmap_t *dst, { 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); + (*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 / 4) + 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 / 4) + 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->clip.left; + dst_y += dst->clip.top; + 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) { + 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) { + 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->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 diff --git a/twin_matrix.c b/twin_matrix.c index e0f18e9..20780ec 100644 --- a/twin_matrix.c +++ b/twin_matrix.c @@ -80,6 +80,14 @@ twin_matrix_identity (twin_matrix_t *m) m->m[2][0] = 0; m->m[2][1] = 0; } +twin_bool_t +twin_matrix_is_identity (twin_matrix_t *m) +{ + return m->m[0][0] == TWIN_FIXED_ONE && m->m[0][1] == 0 && + m->m[1][0] == 0 && m->m[1][1] == TWIN_FIXED_ONE && + m->m[2][0] == 0 && m->m[2][1] == 0; +} + void twin_matrix_translate (twin_matrix_t *m, twin_fixed_t tx, twin_fixed_t ty) { @@ -170,6 +178,22 @@ _twin_matrix_y (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y) return s; } +twin_fixed_t +_twin_matrix_fx (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y) +{ + return twin_fixed_mul (m->m[0][0], x) + + twin_fixed_mul (m->m[1][0], y) + + m->m[2][0]; +} + +twin_fixed_t +_twin_matrix_fy (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y) +{ + return twin_fixed_mul (m->m[0][1], x) + + twin_fixed_mul (m->m[1][1], y) + + m->m[2][1]; +} + twin_sfixed_t _twin_matrix_dx (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y) { diff --git a/twin_pixmap.c b/twin_pixmap.c index 8cd00ac..b390ad6 100644 --- a/twin_pixmap.c +++ b/twin_pixmap.c @@ -39,6 +39,7 @@ twin_pixmap_create (twin_format_t format, pixmap->format = format; pixmap->width = width; pixmap->height = height; + twin_matrix_identity(&pixmap->transform); pixmap->clip.left = pixmap->clip.top = 0; pixmap->clip.right = pixmap->width; pixmap->clip.bottom = pixmap->height; @@ -66,6 +67,7 @@ twin_pixmap_create_const (twin_format_t format, pixmap->format = format; pixmap->width = width; pixmap->height = height; + twin_matrix_identity(&pixmap->transform); pixmap->clip.left = pixmap->clip.top = 0; pixmap->clip.right = pixmap->width; pixmap->clip.bottom = pixmap->height; @@ -242,6 +244,7 @@ static twin_argb32_t _twin_pixmap_fetch (twin_pixmap_t *pixmap, twin_coord_t x, twin_coord_t y) { twin_pointer_t p = twin_pixmap_pointer (pixmap, x - pixmap->x, y - pixmap->y); + // XXX FIX FOR TRANSFORM if (pixmap->x <= x && x < pixmap->x + pixmap->width && pixmap->y <= y && y < pixmap->y + pixmap->height) @@ -36,6 +36,7 @@ typedef int32_t twin_dfixed_t; /* 24.8 format (12.4 * 12.4) */ #define twin_sfixed_floor(f) ((f) & ~0xf) #define twin_sfixed_trunc(f) ((f) >> 4) #define twin_sfixed_ceil(f) (((f) + 0xf) & ~0xf) +#define twin_sfixed_mod(f) ((f) & 0xf) #define twin_int_to_sfixed(i) ((twin_sfixed_t) ((i) * 16)) @@ -108,6 +109,15 @@ typedef void (*twin_src_op) (twin_pointer_t dst, twin_source_u src, int width); +typedef struct _twin_xform { + twin_pixmap_t *pixmap; + twin_pointer_t span; + twin_coord_t left; + twin_coord_t width; + twin_coord_t src_x; + twin_coord_t src_y; +} twin_xform_t; + /* twin_primitive.c */ typedef void twin_in_op_func (twin_pointer_t dst, @@ -302,6 +312,12 @@ _twin_matrix_x (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y); twin_sfixed_t _twin_matrix_y (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y); +twin_fixed_t +_twin_matrix_fx (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y); + +twin_fixed_t +_twin_matrix_fy (twin_matrix_t *m, twin_fixed_t x, twin_fixed_t y); + twin_sfixed_t _twin_matrix_dx (twin_matrix_t *m, twin_fixed_t dx, twin_fixed_t dy); |