summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt benh@kernel.crashing.org <benh@tika.localdomain>2006-11-28 19:43:50 +1100
committerBenjamin Herrenschmidt <benh@tika.localdomain>2006-11-28 19:43:50 +1100
commita9ae3dc5ea52621f7092a60168c5da794891847b (patch)
tree52357bb5840b60bcb13b615a197fc6e10c5b592f
parent56d2a170bcddf2906d7ce6b5ba27a14c44947013 (diff)
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.h22
-rw-r--r--twin_draw.c345
-rw-r--r--twin_matrix.c24
-rw-r--r--twin_pixmap.c3
-rw-r--r--twinint.h16
5 files changed, 369 insertions, 41 deletions
diff --git a/twin.h b/twin.h
index de8f8d7..b7fbe5b 100644
--- a/twin.h
+++ b/twin.h
@@ -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)
diff --git a/twinint.h b/twinint.h
index 3889253..aa46686 100644
--- a/twinint.h
+++ b/twinint.h
@@ -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);