diff --git a/src/Makefile.am b/src/Makefile.am index 32b2df2..3ad7b98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,6 +34,7 @@ cairo_base_sources = \ cairo-misc.c \ cairo-malloc-private.h \ cairo-matrix.c \ + cairo-transform.c \ cairo-mutex.c \ cairo-mutex-private.h \ cairo-mutex-type-private.h \ diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h index 2590048..0a5c67b 100644 --- a/src/cairo-gstate-private.h +++ b/src/cairo-gstate-private.h @@ -59,8 +59,7 @@ struct _cairo_gstate { cairo_surface_t *parent_target; /* The previous target which was receiving rendering */ cairo_surface_t *original_target; /* The original target the initial gstate was created with */ - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; + cairo_transform_t transform; cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */ cairo_pattern_t *source; diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 5049399..196b676 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -99,7 +99,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, gstate->original_target = cairo_surface_reference (target); _cairo_gstate_identity_matrix (gstate); - gstate->source_ctm_inverse = gstate->ctm_inverse; + cairo_matrix_init_identity (&gstate->source_ctm_inverse); gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK, CAIRO_CONTENT_COLOR); @@ -166,8 +166,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) gstate->parent_target = NULL; gstate->original_target = cairo_surface_reference (other->original_target); - gstate->ctm = other->ctm; - gstate->ctm_inverse = other->ctm_inverse; + gstate->transform = other->transform; gstate->source_ctm_inverse = other->source_ctm_inverse; gstate->source = cairo_pattern_reference (other->source); @@ -431,13 +430,19 @@ cairo_status_t _cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source) { + cairo_status_t status; + if (source->status) return source->status; source = cairo_pattern_reference (source); cairo_pattern_destroy (gstate->source); gstate->source = source; - gstate->source_ctm_inverse = gstate->ctm_inverse; + + status = cairo_transform_get_inverse (&gstate->transform, + &gstate->source_ctm_inverse); + if (status) + return status; return CAIRO_STATUS_SUCCESS; } @@ -617,165 +622,85 @@ _cairo_gstate_get_miter_limit (cairo_gstate_t *gstate) void _cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { - *matrix = gstate->ctm; + *matrix = gstate->transform.matrix; } cairo_status_t _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) { - cairo_matrix_t tmp; - - if (! ISFINITE (tx) || ! ISFINITE (ty)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_translate (&tmp, tx, ty); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - cairo_matrix_init_translate (&tmp, -tx, -ty); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; + return cairo_transform_translate (&gstate->transform, tx, ty); } cairo_status_t _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) { - cairo_matrix_t tmp; - - if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */ - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - if (! ISFINITE (sx) || ! ISFINITE (sy)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_scale (&tmp, sx, sy); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - cairo_matrix_init_scale (&tmp, 1/sx, 1/sy); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; + return cairo_transform_scale (&gstate->transform, sx, sy); } cairo_status_t _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) { - cairo_matrix_t tmp; - - if (angle == 0.) - return CAIRO_STATUS_SUCCESS; - - if (! ISFINITE (angle)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_rotate (&tmp, angle); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - cairo_matrix_init_rotate (&tmp, -angle); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; + return cairo_transform_rotate (&gstate->transform, angle); } cairo_status_t _cairo_gstate_transform (cairo_gstate_t *gstate, const cairo_matrix_t *matrix) { - cairo_matrix_t tmp; - cairo_status_t status; - - tmp = *matrix; - status = cairo_matrix_invert (&tmp); - if (status) - return status; - _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - /* paranoid check against gradual numerical instability */ - if (! _cairo_matrix_is_invertible (&gstate->ctm)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - - return CAIRO_STATUS_SUCCESS; + return cairo_transform_transform (&gstate->transform, matrix); } cairo_status_t _cairo_gstate_set_matrix (cairo_gstate_t *gstate, const cairo_matrix_t *matrix) { - cairo_status_t status; - - if (! _cairo_matrix_is_invertible (matrix)) - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - _cairo_gstate_unset_scaled_font (gstate); - - gstate->ctm = *matrix; - gstate->ctm_inverse = *matrix; - status = cairo_matrix_invert (&gstate->ctm_inverse); - assert (status == CAIRO_STATUS_SUCCESS); - - return CAIRO_STATUS_SUCCESS; + return cairo_transform_set_matrix (&gstate->transform, matrix); } void _cairo_gstate_identity_matrix (cairo_gstate_t *gstate) { _cairo_gstate_unset_scaled_font (gstate); - - cairo_matrix_init_identity (&gstate->ctm); - cairo_matrix_init_identity (&gstate->ctm_inverse); + cairo_transform_init_identity (&gstate->transform); } void _cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y) { - cairo_matrix_transform_point (&gstate->ctm, x, y); + cairo_transform_transform_point (&gstate->transform, x, y); } void _cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy) { - cairo_matrix_transform_distance (&gstate->ctm, dx, dy); + cairo_transform_transform_distance (&gstate->transform, dx, dy); } void _cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y) { - cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); + /* XXX: Handle return value */ + cairo_transform_inverse_transform_point (&gstate->transform, x, y); } void _cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy) { - cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); + /* XXX: Handle return value */ + cairo_transform_inverse_transform_distance (&gstate->transform, dx, dy); } void _cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) { - cairo_matrix_transform_point (&gstate->ctm, x, y); + cairo_transform_transform_point (&gstate->transform, x, y); cairo_matrix_transform_point (&gstate->target->device_transform, x, y); } @@ -783,7 +708,7 @@ void _cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y); - cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); + cairo_transform_transform_point (&gstate->transform, x, y); } void @@ -793,10 +718,15 @@ _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, cairo_bool_t *is_tight) { cairo_matrix_t matrix_inverse; + cairo_status_t status; + status = cairo_transform_get_inverse (&gstate->transform, &matrix_inverse); + /* XXX: Handle error code */ + + /* XXX: Transform */ cairo_matrix_multiply (&matrix_inverse, &gstate->target->device_transform_inverse, - &gstate->ctm_inverse); + &matrix_inverse); _cairo_matrix_transform_bounding_box (&matrix_inverse, x1, y1, x2, y2, is_tight); } @@ -829,6 +759,7 @@ _cairo_gstate_path_extents (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; } +/* XXX: Transform */ static cairo_status_t _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern, @@ -869,9 +800,16 @@ _cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, cairo_pattern_t *pattern, cairo_pattern_t *mask) { + cairo_matrix_t matrix_inverse; + cairo_status_t status; + + status = cairo_transform_get_inverse (&gstate->transform, &matrix_inverse); + if (status) + return status; + return _cairo_gstate_copy_transformed_pattern (gstate, pattern, mask, - &gstate->ctm_inverse); + &matrix_inverse); } cairo_status_t @@ -940,6 +878,7 @@ CLEANUP_SOURCE: cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { + cairo_matrix_t matrix_inverse; cairo_status_t status; cairo_pattern_union_t source_pattern; @@ -958,13 +897,18 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) if (status) return status; + status = cairo_transform_get_inverse (&gstate->transform, &matrix_inverse); + if (status) + return status; + + /* XXX: Transform */ status = _cairo_surface_stroke (gstate->target, gstate->op, &source_pattern.base, path, &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, + &gstate->transform.matrix, + &matrix_inverse, gstate->tolerance, gstate->antialias); @@ -983,6 +927,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, { cairo_status_t status; cairo_traps_t traps; + cairo_matrix_t matrix_inverse; if (gstate->stroke_style.line_width <= 0.0) { *inside_ret = FALSE; @@ -993,10 +938,15 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, _cairo_traps_init (&traps); + status = cairo_transform_get_inverse (&gstate->transform, &matrix_inverse); + if (status) + goto BAIL; + + /* XXX: Transform */ status = _cairo_path_fixed_stroke_to_traps (path, &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, + &gstate->transform.matrix, + &matrix_inverse, gstate->tolerance, &traps); if (status) @@ -1125,6 +1075,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, { cairo_status_t status; cairo_traps_t traps; + cairo_matrix_t matrix_inverse; if (gstate->stroke_style.line_width <= 0.0) { if (x1) @@ -1140,14 +1091,20 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, _cairo_traps_init (&traps); - status = _cairo_path_fixed_stroke_to_traps (path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - &traps); - if (status == CAIRO_STATUS_SUCCESS) { - _cairo_gstate_traps_extents_to_user_rectangle(gstate, &traps, x1, y1, x2, y2); + status = cairo_transform_get_inverse (&gstate->transform, &matrix_inverse); + if (status) + { + /* XXX: Transform */ + status = _cairo_path_fixed_stroke_to_traps (path, + &gstate->stroke_style, + &gstate->transform.matrix, + &matrix_inverse, + gstate->tolerance, + &traps); + if (status == CAIRO_STATUS_SUCCESS) { + _cairo_gstate_traps_extents_to_user_rectangle(gstate, &traps, + x1, y1, x2, y2); + } } _cairo_traps_fini (&traps); @@ -1453,9 +1410,10 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) cairo_surface_get_font_options (gstate->target, &options); cairo_font_options_merge (&options, &gstate->font_options); + /* XXX: Transform */ scaled_font = cairo_scaled_font_create (gstate->font_face, &gstate->font_matrix, - &gstate->ctm, + &gstate->transform.matrix, &options); status = cairo_scaled_font_status (scaled_font); @@ -1656,7 +1614,8 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, cairo_glyph_t *transformed_glyphs) { int i; - cairo_matrix_t *ctm = &gstate->ctm; + /* XXX: Transform */ + cairo_matrix_t *ctm = &gstate->transform.matrix; cairo_matrix_t *device_transform = &gstate->target->device_transform; if (_cairo_matrix_is_identity (ctm) && diff --git a/src/cairo-transform.c b/src/cairo-transform.c new file mode 100644 index 0000000..3fadcf2 --- /dev/null +++ b/src/cairo-transform.c @@ -0,0 +1,350 @@ +/* cairo - a vector graphics library with display and print output + * + * XXX + * Copyright © 2002 University of Southern California + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * XXX + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Daniel Kraft + */ + +#define _GNU_SOURCE + +#include "cairoint.h" + +/* XXX: Move somewhere shared? */ +#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) +#define ISFINITE(x) isfinite (x) +#else +#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ +#endif + +static void +_cairo_transform_compute_inverse (cairo_transform_t *transform); + +/** + * cairo_transform_transform_distance: + * @transform: a #cairo_transform_t + * @dx: X component of a distance vector. An in/out parameter + * @dy: Y component of a distance vector. An in/out parameter + * + * Transforms the distance vector (@dx,@dy) by the transformation + * described in @transform. A call to this function is + * equivalent to the following: + * + * cairo_matrix_transform_distance (&transform->matrix, dx, dy); + * + **/ +void +cairo_transform_transform_distance (const cairo_transform_t *transform, + double *dx, double *dy) +{ + cairo_matrix_transform_distance (&transform->matrix, dx, dy); +} +/* XXX +slim_hidden_def(cairo_transform_transform_distance); +*/ + +/** + * cairo_transform_transform_point: + * @transform: a #cairo_transform_t + * @x: X position. An in/out parameter + * @y: Y position. An in/out parameter + * + * Transforms the point (@x, @y) by the transformation + * described in @transform. This function is equivalent + * to the following: + * + * cairo_matrix_transform_point (&transform->matrix, x, y); + * + **/ +void +cairo_transform_transform_point (const cairo_transform_t *transform, + double *x, double *y) +{ + cairo_matrix_transform_point (&transform->matrix, x, y); +} +/* XXX +slim_hidden_def(cairo_transform_transform_point); +*/ + +/** + * cairo_transform_inverse_transform_distance: + * @transform: a #cairo_transform_t + * @dx: X component of a distance vector. An in/out parameter + * @dy: Y component of a distance vector. An in/out parameter + * + * Transforms the distance vector (@dx,@dy) by the inverse + * transformation of @transform. If @transform is not + * invertible, this raises an error state. + **/ +cairo_status_t +cairo_transform_inverse_transform_distance (const cairo_transform_t *transform, + double *dx, double *dy) +{ + if (! transform->has_inverse) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_transform_distance (&transform->inverse, dx, dy); + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_inverse_transform_distance); +*/ + +/** + * cairo_transform_inverse_transform_point: + * @transform: a #cairo_transform_t + * @x: X position. An in/out parameter + * @y: Y position. An in/out parameter + * + * Transforms the point (@x, @y) by the inverse transformation + * of @transform. If @transform is not invertible, this raises + * an error state. + **/ +cairo_status_t +cairo_transform_inverse_transform_point (const cairo_transform_t *transform, + double *x, double *y) +{ + if (! transform->has_inverse) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_transform_point (&transform->inverse, x, y); + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_inverse_transform_point); +*/ + +/** + * cairo_transform_translate: + * @transform: a #cairo_transform_t + * @tx: X offset to translate + * @ty: Y offset to translate + * + * Apply the translation to the current transformation. + **/ +cairo_status_t +cairo_transform_translate (cairo_transform_t *transform, double tx, double ty) +{ + if (! ISFINITE (tx) || ! ISFINITE (ty)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_translate (&transform->matrix, tx, ty); + if (transform->has_inverse) + { + /* XXX: What's about those checks? */ + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&transform->matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_translate (&transform->inverse, -tx, -ty); + } + + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_translate); +*/ + +/** + * cairo_transform_scale: + * @transform: a #cairo_transform_t + * @sx: X factor + * @sy: Y factor + * + * Scale the current transformation. A factor + * of zero is possible but has the effect of + * setting has_inverse to false. + **/ +cairo_status_t +cairo_transform_scale (cairo_transform_t *transform, double sx, double sy) +{ + if (! ISFINITE (sx) || ! ISFINITE (sy)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_scale (&transform->matrix, sx, sy); + + /* If either factor was zero, matrix is no longer invertible */ + if (sx * sy == 0.) + transform->has_inverse = FALSE; + + if (transform->has_inverse) + { + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&transform->matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_translate (&transform->inverse, 1/sx, 1/sy); + } + + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_scale); +*/ + +/** + * cairo_transform_rotate: + * @transform: a #cairo_transform_t + * @angle: Angle to rotate by + * + * Rotate the current transformation. + **/ +cairo_status_t +cairo_transform_rotate (cairo_transform_t *transform, double angle) +{ + if (angle == 0.) + return CAIRO_STATUS_SUCCESS; + + if (! ISFINITE (angle)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_rotate (&transform->matrix, angle); + + if (transform->has_inverse) + { + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&transform->matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_rotate (&transform->inverse, -angle); + } + + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_rotate); +*/ + +/** + * cairo_transform_transform: + * @transform: a #cairo_transform_t + * @matrix: The transformation matrix to apply + * + * Apply another transformation represented by @matrix + * to the current transformation. + **/ +cairo_status_t +cairo_transform_transform (cairo_transform_t *transform, + const cairo_matrix_t* matrix) +{ + cairo_matrix_multiply (&transform->matrix, matrix, &transform->matrix); + _cairo_transform_compute_inverse (transform); + /* XXX: Do we need a paranoid check here? */ + + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_transform); +*/ + +/** + * cairo_transform_set_matrix: + * @transform: a #cairo_transform_t + * @matrix: The transformation matrix to set to + * + * Explicitelly set the transformation matrix + * of @transform and compute the inverse if + * possible. + **/ +cairo_status_t +cairo_transform_set_matrix (cairo_transform_t *transform, + const cairo_matrix_t* matrix) +{ + transform->matrix = *matrix; + _cairo_transform_compute_inverse (transform); + /* XXX: Do we need a paranoid check here? */ + + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_set_matrix); +*/ + +/** + * cairo_transform_get_inverse: + * @transform: a #cairo_transform_t + * @inverse: Store the inverse matrix here + * + * Retrieve the inverse matrix of this transformation; + * returns error code if not invertible. + **/ +cairo_status_t +cairo_transform_get_inverse (const cairo_transform_t *transform, + cairo_matrix_t *inverse) +{ + if (! transform->has_inverse) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + *inverse = transform->inverse; + return CAIRO_STATUS_SUCCESS; +} +/* XXX +slim_hidden_def(cairo_transform_get_inverse); +*/ + +/** + * cairo_transform_init_identity: + * @transform: a #cairo_transform_t + * + * Initialize @transform to the identity + * mapping. + **/ +void +cairo_transform_init_identity (cairo_transform_t *transform) +{ + cairo_matrix_init_identity (&transform->matrix); + transform->has_inverse = TRUE; + cairo_matrix_init_identity (&transform->inverse); +} +/* XXX +slim_hidden_def(cairo_transform_init_identity); +*/ + +/** + * _cairo_transform_compute_inverse: + * @transform: a #cairo_transform_t + * + * Compute has_inverse and if possible inverse + * of @transform from the matrix of @transform. + **/ +static void +_cairo_transform_compute_inverse (cairo_transform_t *transform) +{ + cairo_status_t status; + + transform->inverse = transform->matrix; + status = cairo_matrix_invert (&transform->inverse); + + /* XXX: Is this ok, or checks for other errors needed? */ + transform->has_inverse = (status == CAIRO_STATUS_SUCCESS); +} diff --git a/src/cairo.h b/src/cairo.h index ab730e7..5a9ce23 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -132,6 +132,22 @@ typedef struct _cairo_matrix { } cairo_matrix_t; /** + * cairo_transform_t: + * @matrix: The cairo_matrix_t doing the transformation + * @inverse: The inverse matrix if it exists + * @has_inverse: Whether the matrix has an inverse + * + * A #cairo_transform_t holds a coordinate transformation + * consisting of a transformation matrix. If this matrix + * is invertible, it also stores the inverse of the matrix. + **/ +typedef struct _cairo_transform { + cairo_matrix_t matrix; + cairo_matrix_t inverse; + cairo_bool_t has_inverse; +} cairo_transform_t; + +/** * cairo_pattern_t: * * A #cairo_pattern_t represents a source when drawing onto a @@ -1923,6 +1939,48 @@ cairo_public void cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y); +/* Transform functions */ + +cairo_public void +cairo_transform_transform_distance (const cairo_transform_t *transform, + double *dx, double *dy); + +cairo_public void +cairo_transform_transform_point (const cairo_transform_t *transform, + double *x, double *y); + +cairo_public cairo_status_t +cairo_transform_inverse_transform_distance (const cairo_transform_t *transform, + double *dx, double *dy); + +cairo_public cairo_status_t +cairo_transform_inverse_transform_point (const cairo_transform_t *transform, + double *x, double *y); + +cairo_public cairo_status_t +cairo_transform_translate (cairo_transform_t *transform, double tx, double ty); + +cairo_public cairo_status_t +cairo_transform_scale (cairo_transform_t *transform, double sx, double sy); + +cairo_public cairo_status_t +cairo_transform_rotate (cairo_transform_t *transform, double angle); + +cairo_public cairo_status_t +cairo_transform_transform (cairo_transform_t *transform, + const cairo_matrix_t *matrix); + +cairo_public cairo_status_t +cairo_transform_set_matrix (cairo_transform_t *transform, + const cairo_matrix_t *matrix); + +cairo_public cairo_status_t +cairo_transform_get_inverse (const cairo_transform_t *transform, + cairo_matrix_t *inverse); + +cairo_public void +cairo_transform_init_identity (cairo_transform_t *transform); + /* Functions to be used while debugging (not intended for use in production code) */ cairo_public void cairo_debug_reset_static_data (void);