| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can |
| * be found in the LICENSE file. |
| * |
| */ |
| |
| #include "transform_stack.h" |
| |
| // |
| // |
| // |
| |
| #include <stdlib.h> |
| #include <math.h> |
| |
| // |
| // |
| // |
| |
| #undef NDEBUG |
| #include <assert.h> |
| |
| // |
| // |
| // |
| |
| #define TS_TRANSFORM_SUFFIX_EVAL(a) a |
| |
| #define TS_TRANSFORM_SUFFIX_CONCAT(func) \ |
| TS_TRANSFORM_SUFFIX_EVAL(func)##TS_TRANSFORM_SUFFIX_EVAL(TS_TRANSFORM_FLOAT_SUFFIX) |
| |
| // |
| // |
| // |
| |
| #define TS_TRANSFORM_SIN(x) TS_TRANSFORM_SUFFIX_CONCAT(sin)(x) |
| #define TS_TRANSFORM_COS(x) TS_TRANSFORM_SUFFIX_CONCAT(cos)(x) |
| #define TS_TRANSFORM_TAN(x) TS_TRANSFORM_SUFFIX_CONCAT(tan)(x) |
| |
| // |
| // |
| // |
| #define TS_TRANSFORM_ZERO ((ts_transform_float_t)0.0) |
| #define TS_TRANSFORM_ONE ((ts_transform_float_t)1.0) |
| #define TS_TRANSFORM_RCP(f) (TS_TRANSFORM_ONE / (f)) |
| |
| |
| // |
| // |
| // |
| |
| union ts_transform_stack_3x3_u |
| { |
| ts_transform_float_t a8[8]; |
| |
| struct { |
| ts_transform_float_t sx; |
| ts_transform_float_t shx; |
| ts_transform_float_t tx; |
| |
| ts_transform_float_t shy; |
| ts_transform_float_t sy; |
| ts_transform_float_t ty; |
| |
| ts_transform_float_t w0; |
| ts_transform_float_t w1; |
| // w2 is always 1.0 |
| }; |
| |
| struct { |
| ts_transform_float_t a; |
| ts_transform_float_t b; |
| ts_transform_float_t c; |
| |
| ts_transform_float_t d; |
| ts_transform_float_t e; |
| ts_transform_float_t f; |
| |
| ts_transform_float_t g; |
| ts_transform_float_t h; |
| // i is always 1.0 |
| }; |
| }; |
| |
| // |
| // |
| // |
| |
| struct ts_transform_stack |
| { |
| uint32_t size; |
| uint32_t count; |
| |
| ts_transform_weakref_t * weakrefs; |
| union ts_transform_stack_3x3_u * transforms; |
| }; |
| |
| // |
| // |
| // |
| |
| static |
| void |
| ts_transform_stack_resize(struct ts_transform_stack * const ts, uint32_t const size) |
| { |
| ts->size = size; |
| ts->weakrefs = realloc(ts->weakrefs, size * sizeof(*ts->weakrefs)); |
| ts->transforms = realloc(ts->transforms,size * sizeof(*ts->transforms)); |
| } |
| |
| static |
| void |
| ts_transform_stack_ensure(struct ts_transform_stack * const ts) |
| { |
| if (ts->count < ts->size) |
| return; |
| |
| // increase by 50% and by at least 8 |
| ts_transform_stack_resize(ts,ts->size + max(ts->size/2,8)); |
| } |
| |
| // |
| // |
| // |
| |
| struct ts_transform_stack * |
| ts_transform_stack_create(uint32_t const size) |
| { |
| struct ts_transform_stack * ts = malloc(sizeof(*ts)); |
| |
| ts->size = size; |
| ts->count = 0; |
| |
| ts->transforms = NULL; |
| ts->weakrefs = NULL; |
| |
| ts_transform_stack_resize(ts,size); |
| |
| return ts; |
| } |
| |
| void |
| ts_transform_stack_release(struct ts_transform_stack * const ts) |
| { |
| free(ts->transforms); |
| free(ts->weakrefs); |
| |
| free(ts); |
| } |
| |
| // |
| // |
| // |
| |
| uint32_t |
| ts_transform_stack_save(struct ts_transform_stack * const ts) |
| { |
| return ts->count; |
| } |
| |
| void |
| ts_transform_stack_restore(struct ts_transform_stack * const ts, |
| uint32_t const restore) |
| { |
| ts->count = restore; |
| } |
| |
| // |
| // |
| // |
| |
| static |
| union ts_transform_stack_3x3_u * |
| ts_transform_stack_tos(struct ts_transform_stack * const ts) |
| { |
| return ts->transforms + ts->count - 1; |
| } |
| |
| // |
| // |
| // |
| |
| static |
| void |
| ts_transform_stack_3x3_u_copy(union ts_transform_stack_3x3_u * const __restrict dst, |
| union ts_transform_stack_3x3_u const * const __restrict src) |
| { |
| *dst = *src; |
| } |
| |
| // |
| // C = A * B |
| // |
| // FIXME -- can save affine vs. projective flags and save a few ops |
| // |
| |
| #define TS_TRANSFORM_MULTIPLY(A,B) \ |
| A->sx * B->sx + A->shx * B->shy + A->tx * B->w0, \ |
| A->sx * B->shx + A->shx * B->sy + A->tx * B->w1, \ |
| A->sx * B->tx + A->shx * B->ty + A->tx, \ |
| A->shy * B->sx + A->sy * B->shy + A->ty * B->w0, \ |
| A->shy * B->shx + A->sy * B->sy + A->ty * B->w1, \ |
| A->shy * B->tx + A->sy * B->ty + A->ty, \ |
| A->w0 * B->sx + A->w1 * B->shy + B->w0, \ |
| A->w0 * B->shx + A->w1 * B->sy + B->w1, \ |
| A->w0 * B->tx + A->w1 * B->ty + TS_TRANSFORM_ONE |
| |
| // |
| // |
| // |
| |
| #define TS_IS_AFFINE(t) ((t->w0 == TS_TRANSFORM_ZERO) && (t->w1 == TS_TRANSFORM_ZERO)) |
| |
| static |
| ts_transform_type_e |
| ts_transform_stack_classify(struct ts_transform_stack * const ts) |
| { |
| union ts_transform_stack_3x3_u const * const t = ts_transform_stack_tos(ts); |
| |
| if (TS_IS_AFFINE(t)) |
| return TS_TRANSFORM_TYPE_AFFINE; |
| else |
| return TS_TRANSFORM_TYPE_PROJECTIVE; |
| } |
| |
| // |
| // |
| // |
| |
| ts_transform_float_t * |
| ts_transform_stack_top_transform(struct ts_transform_stack * const ts) |
| { |
| return ts_transform_stack_tos(ts)->a8; |
| } |
| |
| ts_transform_weakref_t * |
| ts_transform_stack_top_weakref(struct ts_transform_stack * const ts) |
| { |
| return ts->weakrefs + ts->count - 1; |
| } |
| |
| // |
| // |
| // |
| |
| void |
| ts_transform_stack_dup(struct ts_transform_stack * const ts) |
| { |
| ts_transform_stack_ensure(ts); |
| |
| union ts_transform_stack_3x3_u * const tos = ts_transform_stack_tos(ts); |
| |
| ts_transform_stack_3x3_u_copy(tos+1,tos); |
| |
| ts->weakrefs[ts->count] = ts->weakrefs[ts->count-1]; |
| |
| ts->count += 1; |
| } |
| |
| void |
| ts_transform_stack_drop(struct ts_transform_stack * const ts) |
| { |
| assert(ts->count >= 1); |
| |
| ts->count -= 1; |
| } |
| |
| // |
| // |
| // |
| |
| static |
| void |
| ts_transform_stack_swap_drop(struct ts_transform_stack * const ts) |
| { |
| assert(ts->count >= 2); |
| |
| union ts_transform_stack_3x3_u * const tos = ts_transform_stack_tos(ts); |
| |
| ts_transform_stack_3x3_u_copy(tos-1,tos); |
| |
| ts->weakrefs[ts->count-2] = ts->weakrefs[ts->count-1]; |
| |
| ts->count -= 1; |
| } |
| |
| // |
| // |
| // |
| |
| static |
| void |
| ts_transform_stack_store_matrix_8(struct ts_transform_stack * const ts, |
| uint32_t const idx, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const shx, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const shy, |
| ts_transform_float_t const sy, |
| ts_transform_float_t const ty, |
| ts_transform_float_t const w0, |
| ts_transform_float_t const w1) |
| { |
| union ts_transform_stack_3x3_u * t = ts->transforms + idx; |
| |
| t->sx = sx; |
| t->shx = shx; |
| t->tx = tx; |
| |
| t->shy = shy; |
| t->sy = sy; |
| t->ty = ty; |
| |
| t->w0 = w0; |
| t->w1 = w1; |
| |
| ts->weakrefs[idx] = TS_TRANSFORM_WEAKREF_INVALID; |
| } |
| |
| // |
| // |
| // |
| |
| static |
| void |
| ts_transform_stack_store_matrix(struct ts_transform_stack * const ts, |
| uint32_t const idx, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const shx, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const shy, |
| ts_transform_float_t const sy, |
| ts_transform_float_t const ty, |
| ts_transform_float_t const w0, |
| ts_transform_float_t const w1, |
| ts_transform_float_t const w2) |
| { |
| if (w2 == TS_TRANSFORM_ONE) |
| { |
| ts_transform_stack_store_matrix_8(ts,idx, |
| sx, shx,tx, |
| shy,sy, ty, |
| w0, w1); |
| } |
| else |
| { |
| // normalize |
| ts_transform_float_t d = TS_TRANSFORM_RCP(w2); |
| |
| ts_transform_stack_store_matrix_8(ts,idx, |
| sx * d, shx * d, tx * d, |
| shy * d, sy * d, ty * d, |
| w0 * d, w1 * d); |
| } |
| } |
| |
| // |
| // |
| // |
| |
| static |
| void |
| ts_transform_stack_push_matrix_8(struct ts_transform_stack * const ts, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const shx, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const shy, |
| ts_transform_float_t const sy, |
| ts_transform_float_t const ty, |
| ts_transform_float_t const w0, |
| ts_transform_float_t const w1) |
| { |
| ts_transform_stack_ensure(ts); |
| |
| ts_transform_stack_store_matrix_8(ts,ts->count++, |
| sx, shx,tx, |
| shy,sy, ty, |
| w0, w1); |
| } |
| |
| // |
| // |
| // |
| |
| void |
| ts_transform_stack_push_matrix(struct ts_transform_stack * const ts, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const shx, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const shy, |
| ts_transform_float_t const sy, |
| ts_transform_float_t const ty, |
| ts_transform_float_t const w0, |
| ts_transform_float_t const w1, |
| ts_transform_float_t const w2) |
| { |
| if (w2 == TS_TRANSFORM_ONE) |
| { |
| ts_transform_stack_push_matrix_8(ts, |
| sx, shx,tx, |
| shy,sy, ty, |
| w0, w1); |
| } |
| else |
| { |
| // normalize |
| ts_transform_float_t d = TS_TRANSFORM_RCP(w2); |
| |
| ts_transform_stack_push_matrix_8(ts, |
| sx * d, shx * d, tx * d, |
| shy * d, sy * d, ty * d, |
| w0 * d, w1 * d); |
| } |
| } |
| |
| // |
| // |
| // |
| |
| void |
| ts_transform_stack_push_identity(struct ts_transform_stack * const ts) |
| { |
| ts_transform_stack_push_matrix_8(ts, |
| 1.0, 0.0, 0.0, |
| 0.0, 1.0, 0.0, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_affine(struct ts_transform_stack * const ts, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const shx, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const shy, |
| ts_transform_float_t const sy, |
| ts_transform_float_t const ty) |
| { |
| ts_transform_stack_push_matrix_8(ts, |
| sx, shx, tx, |
| shy, sy, ty, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_translate(struct ts_transform_stack * const ts, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const ty) |
| { |
| ts_transform_stack_push_matrix_8(ts, |
| 1.0, 0.0, tx, |
| 0.0, 1.0, ty, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_scale(struct ts_transform_stack * const ts, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const sy) |
| { |
| ts_transform_stack_push_matrix_8(ts, |
| sx, 0.0, 0.0, |
| 0.0, sy, 0.0, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_shear(struct ts_transform_stack * const ts, |
| ts_transform_float_t const shx, |
| ts_transform_float_t const shy) |
| { |
| ts_transform_stack_push_matrix_8(ts, |
| 1.0, shx, 0.0, |
| shy, 1.0, 0.0, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_skew_x(struct ts_transform_stack * const ts, |
| ts_transform_float_t const theta) |
| { |
| ts_transform_float_t const tan_theta = TS_TRANSFORM_TAN(theta); // replace with tanpi if available |
| |
| ts_transform_stack_push_matrix_8(ts, |
| 1.0, tan_theta,0.0, |
| 0.0, 1.0, 0.0, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_skew_y(struct ts_transform_stack * const ts, |
| ts_transform_float_t const theta) |
| { |
| ts_transform_float_t const tan_theta = TS_TRANSFORM_TAN(theta); // replace with tanpi if available |
| |
| ts_transform_stack_push_matrix_8(ts, |
| 1.0, 0.0, 0.0, |
| tan_theta, 1.0, 0.0, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_rotate(struct ts_transform_stack * const ts, |
| ts_transform_float_t const theta) |
| { |
| ts_transform_float_t const cos_theta = TS_TRANSFORM_COS(theta); // replace with cospi if available |
| ts_transform_float_t const sin_theta = TS_TRANSFORM_SIN(theta); // replace with sinpi if available |
| |
| ts_transform_stack_push_matrix_8(ts, |
| cos_theta,-sin_theta, 0.0, |
| sin_theta, cos_theta, 0.0, |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_rotate_xy2(struct ts_transform_stack * const ts, |
| ts_transform_float_t const theta, |
| ts_transform_float_t const cx, |
| ts_transform_float_t const cy, |
| ts_transform_float_t const tx, |
| ts_transform_float_t const ty) |
| { |
| ts_transform_float_t const cos_theta = TS_TRANSFORM_COS(theta); // replace with cospi if available |
| ts_transform_float_t const sin_theta = TS_TRANSFORM_SIN(theta); // replace with sinpi if available |
| |
| ts_transform_stack_push_matrix_8(ts, |
| cos_theta,-sin_theta, tx - (cx * cos_theta) + (cy * sin_theta), |
| sin_theta, cos_theta, ty - (cx * sin_theta) - (cy * cos_theta), |
| 0.0, 0.0); |
| } |
| |
| void |
| ts_transform_stack_push_rotate_xy(struct ts_transform_stack * const ts, |
| ts_transform_float_t const theta, |
| ts_transform_float_t const cx, |
| ts_transform_float_t const cy) |
| { |
| ts_transform_stack_push_rotate_xy2(ts,theta,cx,cy,cx,cy); |
| } |
| |
| void |
| ts_transform_stack_push_rotate_scale_xy(struct ts_transform_stack * const ts, |
| ts_transform_float_t const theta, |
| ts_transform_float_t const sx, |
| ts_transform_float_t const sy, |
| ts_transform_float_t const cx, |
| ts_transform_float_t const cy) |
| { |
| ts_transform_float_t const cos_theta = TS_TRANSFORM_COS(theta); // replace with cospi if available |
| ts_transform_float_t const sin_theta = TS_TRANSFORM_SIN(theta); // replace with sinpi if available |
| |
| ts_transform_stack_push_matrix_8(ts, |
| sx*cos_theta,-sx*sin_theta, cx - cx*sx*cos_theta + cy*sy*sin_theta, |
| sy*sin_theta, sy*cos_theta, cy - cy*sy*cos_theta - cx*sx*sin_theta, |
| 0.0, 0.0); |
| } |
| |
| // |
| // See: "Fundamentals of Texture Mapping and Image Warping" by Paul S. Heckbert (1989) |
| // |
| |
| #define DET(a,b,c,d) (a * d - b * c) |
| |
| #define X(v,i) v[i*2] |
| #define Y(v,i) v[i*2+1] |
| |
| // |
| // |
| // |
| |
| ts_transform_type_e |
| ts_transform_stack_adjoint(struct ts_transform_stack * const ts) |
| { |
| union ts_transform_stack_3x3_u * const t = ts_transform_stack_tos(ts); |
| |
| #if 0 |
| // save for determinant |
| ts_transform_float_t const a = t->a; |
| ts_transform_float_t const b = t->b; |
| ts_transform_float_t const c = t->c; |
| #endif |
| |
| ts_transform_stack_store_matrix(ts,ts->count-1, |
| |
| +DET(t->e, t->f, t->h, TS_TRANSFORM_ONE), |
| -DET(t->b, t->c, t->h, TS_TRANSFORM_ONE), |
| +DET(t->b, t->c, t->e, t->f), |
| |
| -DET(t->d, t->f, t->g, TS_TRANSFORM_ONE), |
| +DET(t->a, t->c, t->g, TS_TRANSFORM_ONE), |
| -DET(t->a, t->c, t->d, t->f), |
| |
| +DET(t->d, t->e, t->g, t->h), |
| -DET(t->a, t->b, t->g, t->h), |
| +DET(t->a, t->b, t->d, t->e)); |
| |
| #if 0 |
| // determinant of t |
| ts_transform_float_t const det = a * t->a + b * t->d + c * t->g; |
| #endif |
| |
| return ts_transform_stack_classify(ts); |
| } |
| |
| // |
| // |
| // |
| |
| ts_transform_type_e |
| ts_transform_stack_push_unit_to_quad(struct ts_transform_stack * const ts, |
| ts_transform_float_t const quad[8]) |
| { |
| ts_transform_float_t const x0 = X(quad,0); |
| ts_transform_float_t const y0 = Y(quad,0); |
| |
| ts_transform_float_t const x1 = X(quad,1); |
| ts_transform_float_t const y1 = Y(quad,1); |
| |
| ts_transform_float_t const x2 = X(quad,2); |
| ts_transform_float_t const y2 = Y(quad,2); |
| |
| ts_transform_float_t const x3 = X(quad,3); |
| ts_transform_float_t const y3 = Y(quad,3); |
| |
| ts_transform_float_t sx = x1 - x0; |
| ts_transform_float_t shy = y1 - y0; |
| |
| ts_transform_float_t const dx2 = x3 - x2; |
| ts_transform_float_t const dy2 = y3 - y2; |
| |
| ts_transform_float_t const dx3 = -sx - dx2; |
| ts_transform_float_t const dy3 = -shy - dy2; |
| |
| // if both zero then quad_dst is a parallelogram and affine |
| if ((dx3 == TS_TRANSFORM_ZERO) && (dy3 == TS_TRANSFORM_ZERO)) |
| { |
| ts_transform_float_t const shx = x2 - x1; |
| ts_transform_float_t const sy = y2 - y1; |
| |
| ts_transform_stack_push_matrix_8(ts, |
| sx, shx, x0, |
| shy, sy, y0, |
| 0.0, 0.0); |
| |
| return TS_TRANSFORM_TYPE_AFFINE; |
| } |
| else |
| { |
| ts_transform_float_t const dx1 = x1 - x2; |
| ts_transform_float_t const dy1 = y1 - y2; |
| |
| ts_transform_float_t const wx_den = dx1 * dy2 - dx2 * dy1; |
| |
| if (wx_den == TS_TRANSFORM_ZERO) |
| return TS_TRANSFORM_TYPE_INVALID; |
| |
| ts_transform_float_t const w0_num = dx3 * dy2 - dx2 * dy3; |
| ts_transform_float_t const w1_num = dx1 * dy3 - dx3 * dy1; |
| |
| ts_transform_float_t const w0 = w0_num / wx_den; |
| ts_transform_float_t const w1 = w1_num / wx_den; |
| |
| sx += w0 * x1; |
| ts_transform_float_t const shx = x3 - x0 + w1 * x3; |
| |
| shy += w0 * y1; |
| ts_transform_float_t const sy = y3 - y0 + w1 * y3; |
| |
| ts_transform_stack_push_matrix_8(ts, |
| sx, shx, x0, |
| shy, sy, y0, |
| w0, w1); |
| |
| return TS_TRANSFORM_TYPE_PROJECTIVE; |
| } |
| } |
| |
| // |
| // |
| // |
| |
| ts_transform_type_e |
| ts_transform_stack_push_quad_to_unit(struct ts_transform_stack * const ts, |
| float const quad[8]) |
| { |
| if (ts_transform_stack_push_unit_to_quad(ts,quad) == TS_TRANSFORM_TYPE_INVALID) |
| return TS_TRANSFORM_TYPE_INVALID; |
| |
| return ts_transform_stack_adjoint(ts); |
| } |
| |
| // |
| // |
| // |
| |
| ts_transform_type_e |
| ts_transform_stack_push_quad_to_quad(struct ts_transform_stack * const ts, |
| ts_transform_float_t const quad_src[8], |
| ts_transform_float_t const quad_dst[8]) |
| { |
| if (ts_transform_stack_push_unit_to_quad(ts,quad_dst) == TS_TRANSFORM_TYPE_INVALID) |
| return TS_TRANSFORM_TYPE_INVALID; |
| |
| if (ts_transform_stack_push_quad_to_unit(ts,quad_src) == TS_TRANSFORM_TYPE_INVALID) |
| return TS_TRANSFORM_TYPE_INVALID; |
| |
| ts_transform_stack_multiply(ts); |
| |
| return ts_transform_stack_classify(ts); |
| } |
| |
| // |
| // |
| // |
| |
| ts_transform_type_e |
| ts_transform_stack_push_rect_to_quad(struct ts_transform_stack * const ts, |
| ts_transform_float_t const x0, |
| ts_transform_float_t const y0, |
| ts_transform_float_t const x1, |
| ts_transform_float_t const y1, |
| ts_transform_float_t const quad_dst[8]) |
| { |
| if (ts_transform_stack_push_unit_to_quad(ts,quad_dst) == TS_TRANSFORM_TYPE_INVALID) |
| return TS_TRANSFORM_TYPE_INVALID; |
| |
| ts_transform_stack_push_matrix_8(ts, |
| TS_TRANSFORM_RCP(x1-x0), |
| 0.0, |
| -x0, |
| 0.0, |
| TS_TRANSFORM_RCP(y1-y0), |
| -y0, |
| 0.0, |
| 0.0); |
| |
| ts_transform_stack_multiply(ts); |
| |
| return ts_transform_stack_classify(ts); |
| } |
| |
| // |
| // The second matrix on the stack (TOS[-1]) is post-multiplied by the |
| // top matrix on the stack (TOS[0]). |
| // |
| // The result replaces TOS[0] and TOS[-1] is unmodified. |
| // |
| // The stack effect of concat is: |
| // |
| // | B | | A*B | |
| // | A | | A | |
| // | . | => | . | |
| // | . | | . | |
| // | . | | . | |
| // |
| void |
| ts_transform_stack_concat(struct ts_transform_stack * const ts) |
| { |
| assert(ts->count >= 2); |
| |
| // get A and B |
| union ts_transform_stack_3x3_u const * const B = ts_transform_stack_tos(ts); |
| union ts_transform_stack_3x3_u const * const A = B - 1; |
| |
| ts_transform_stack_store_matrix(ts,ts->count-1,TS_TRANSFORM_MULTIPLY(A,B)); |
| } |
| |
| // |
| // The second matrix on the stack (TOS[-1]) is post-multiplied by the |
| // top matrix on the stack (TOS[0]). |
| // |
| // The result replaces both matrices. |
| // |
| // The stack effect of multiply is: |
| // |
| // | B | | A*B | |
| // | A | | . | |
| // | . | => | . | |
| // | . | | . | |
| // | . | | . | |
| // |
| void |
| ts_transform_stack_multiply(struct ts_transform_stack * const ts) |
| { |
| assert(ts->count >= 2); |
| |
| // get A and B |
| union ts_transform_stack_3x3_u const * const B = ts_transform_stack_tos(ts); |
| union ts_transform_stack_3x3_u const * const A = B - 1; |
| |
| ts_transform_stack_store_matrix(ts,ts->count-- - 2,TS_TRANSFORM_MULTIPLY(A,B)); |
| } |
| |
| // |
| // |
| // |
| |
| void |
| ts_transform_stack_transform_xy(struct ts_transform_stack * const ts, |
| ts_transform_float_t const x, |
| ts_transform_float_t const y, |
| ts_transform_float_t * const xp, |
| ts_transform_float_t * const yp) |
| { |
| union ts_transform_stack_3x3_u const * const t = ts_transform_stack_tos(ts); |
| |
| *xp = x * t->sx + y * t->shx + t->tx; |
| *yp = x * t->shy + y * t->sy + t->ty; |
| |
| if (!TS_IS_AFFINE(t)) |
| { |
| ts_transform_float_t const d = TS_TRANSFORM_RCP(x * t->w0 + y * t->w1 + TS_TRANSFORM_ONE); |
| |
| *xp *= d; |
| *yp *= d; |
| } |
| } |
| |
| // |
| // test it! |
| // |
| |
| #ifdef TS_DEBUG |
| |
| #include <stdio.h> |
| |
| #define TS_DEBUG_SCALE 32.0 |
| |
| // |
| // |
| // |
| |
| void |
| ts_transform_stack_tos_debug(struct ts_transform_stack * const ts) |
| { |
| union ts_transform_stack_3x3_u const * const t = ts_transform_stack_tos(ts); |
| |
| printf("{ { %13.5f, %13.5f, %13.5f },\n" |
| " { %13.5f, %13.5f, %13.5f },\n" |
| " { %13.5f, %13.5f, %13.5f } }\n", |
| t->a8[0], |
| t->a8[1], |
| t->a8[2], |
| t->a8[3], |
| t->a8[4], |
| t->a8[5], |
| t->a8[6], |
| t->a8[7], |
| TS_TRANSFORM_ONE); |
| } |
| |
| // |
| // |
| // |
| |
| void |
| ts_debug(struct ts_transform_stack * const ts, |
| ts_transform_float_t const quad[8]) |
| { |
| ts_transform_stack_tos_debug(ts); |
| |
| for (int ii=0; ii<8; ii+=2) |
| { |
| ts_transform_float_t xp,yp; |
| |
| ts_transform_stack_transform_xy(ts, |
| quad[ii],quad[ii+1], |
| &xp,&yp); |
| |
| printf("( %13.2f, %13.2f ) \t-> ( %13.2f, %13.2f )\n", |
| xp,yp,xp/TS_DEBUG_SCALE,yp/TS_DEBUG_SCALE); |
| } |
| } |
| |
| // |
| // |
| // |
| |
| int |
| main(int argc, char * argv[]) |
| { |
| struct ts_transform_stack * const ts = ts_transform_stack_create(32); |
| |
| ts_transform_float_t const w = 1000; |
| ts_transform_float_t const h = 1000; |
| |
| #if 1 |
| ts_transform_stack_push_scale(ts,TS_DEBUG_SCALE,TS_DEBUG_SCALE); |
| |
| // OpenGL'ism |
| ts_transform_stack_push_affine(ts, |
| 1.0f, 0.0f,0.0f, |
| 0.0f,-1.0f,h); |
| // multiply |
| ts_transform_stack_concat(ts); |
| #else |
| ts_transform_stack_push_identity(ts); |
| #endif |
| |
| uint32_t const restore = ts_transform_stack_save(ts); |
| |
| // |
| // |
| // |
| ts_transform_float_t const quad_src[8] = { 0.0f,0.0f, |
| w, 0.0f, |
| w, h, |
| 0.0f,h }; |
| |
| ts_transform_float_t const quad_dst[8] = { 300.0f, 0.0f, |
| w-300.0f, 0.0f, |
| w, h, |
| 0.0f, h }; |
| |
| ts_transform_float_t const quad_tst[8] = { 50, 50, |
| 1550, 50, |
| 1550, 1550, |
| 50, 1550 }; |
| // |
| // RECT TO QUAD |
| // |
| printf("type = %d\n", |
| ts_transform_stack_push_rect_to_quad(ts, |
| 0.0, 0.0, |
| w, h, |
| quad_dst)); |
| ts_transform_stack_concat(ts); |
| |
| ts_debug(ts,quad_src); |
| |
| // |
| // QUAD TO QUAD |
| // |
| ts_transform_stack_restore(ts,restore); |
| |
| printf("type = %d\n", |
| ts_transform_stack_push_quad_to_quad(ts, |
| quad_src, |
| quad_dst)); |
| ts_transform_stack_concat(ts); |
| |
| ts_debug(ts,quad_src); |
| |
| // |
| // DIRECT |
| // |
| ts_transform_stack_restore(ts,restore); |
| |
| ts_transform_stack_push_matrix(ts, |
| 0.87004626f, -0.35519487f, 72.14745f, |
| 0.0f, 0.2600208f, 86.16314f, |
| 0.0f, -0.0029599573f, 1.0f); |
| |
| ts_transform_stack_concat(ts); |
| |
| ts_transform_float_t const quad_foo[8] = { -10, 10, |
| 130, 10, |
| 130, 110, |
| -10, 110 }; |
| |
| ts_debug(ts,quad_foo); |
| |
| return EXIT_SUCCESS; |
| } |
| |
| #endif |
| |
| // |
| // |
| // |