/* cairo - a vector graphics library with display and print output * * 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. * * Author: * Lutz Mueller */ #include "cairo-transform.h" #include "cairoint.h" #include "cairo-path-fixed-private.h" #include "cairo-meta-surface-private.h" static const cairo_surface_backend_t cairo_transform_surface_backend; typedef enum { CAIRO_TRANSFORM_SURFACE_STATE_PROCESS_PAGE = 0, CAIRO_TRANSFORM_SURFACE_STATE_IGNORE_PAGE, CAIRO_TRANSFORM_SURFACE_STATE_BUFFER_PAGE } cairo_transform_surface_state_type_t; typedef struct { cairo_transform_surface_state_type_t type; unsigned int step, num_steps; unsigned int in_pages, out_pages; cairo_bool_t has_page; } cairo_transform_surface_state_t; typedef struct cairo_transform_surface { cairo_surface_t base; cairo_array_t metas; cairo_matrix_t matrix; cairo_array_t surfaces; cairo_array_t pages[2]; cairo_bool_t buffer; cairo_transform_surface_state_t state; } cairo_transform_surface_t; static void _cairo_transform_surface_recalc_state (cairo_transform_surface_t *ts) { assert (!ts->state.step); unsigned int p, i; cairo_status_t s = CAIRO_STATUS_SUCCESS; cairo_surface_t *ss, *b; if (ts->state.out_pages >= ts->pages[0].num_elements) { if (ts->pages[0].num_elements) { ts->state.type = CAIRO_TRANSFORM_SURFACE_STATE_IGNORE_PAGE; return; } ts->state.type = CAIRO_TRANSFORM_SURFACE_STATE_PROCESS_PAGE; ts->state.num_steps = ts->surfaces.num_elements; return; } _cairo_array_copy_element (&ts->pages[0], ts->state.out_pages, &p); if (p + 1 == ts->state.in_pages) { ts->state.type = CAIRO_TRANSFORM_SURFACE_STATE_PROCESS_PAGE; ts->state.num_steps = ts->surfaces.num_elements; return; } if (p + 1 < ts->state.in_pages) { _cairo_array_copy_element (&ts->pages[0], ts->state.out_pages, &p); _cairo_array_copy_element (&ts->metas, p, &b); for (i = 0; !s && (i < ts->surfaces.num_elements); i++) { _cairo_array_copy_element (&ts->surfaces, i, &ss); _cairo_meta_surface_replay (b, ss); _cairo_surface_show_page (ss); } ts->state.out_pages++; ts->state.has_page = 0; _cairo_transform_surface_recalc_state (ts); return; } for (i = ts->state.out_pages + 1; i < ts->pages[0].num_elements; i++) { _cairo_array_copy_element (&ts->pages[0], i, &p); if (p == ts->state.out_pages) { ts->state.type = CAIRO_TRANSFORM_SURFACE_STATE_BUFFER_PAGE; ts->state.num_steps = 1; return; } } ts->state.type = CAIRO_TRANSFORM_SURFACE_STATE_IGNORE_PAGE; } static cairo_surface_t * _cairo_transform_surface_get_meta (cairo_transform_surface_t *ts) { cairo_status_t s; unsigned int n; cairo_surface_t *ss; if (ts->state.in_pages > ts->metas.num_elements) { n = ts->state.in_pages - ts->metas.num_elements; if ((s = _cairo_array_grow_by (&ts->metas, n))) return NULL; memset (&(*ts->metas.elements)[ts->metas.num_elements * ts->metas.element_size], 0, n * ts->metas.element_size); ts->metas.num_elements += n; } _cairo_array_copy_element (&ts->metas, ts->state.in_pages - 1, &ss); if (ss) return ss; ss = _cairo_meta_surface_create (CAIRO_CONTENT_COLOR_ALPHA, 0, 0); memcpy (&(*ts->metas.elements)[(ts->state.in_pages - 1) * ts->metas.element_size], &ss, ts->metas.element_size); return ss; } static cairo_surface_t * _cairo_transform_surface_get_surface_show_page (cairo_transform_surface_t *ts) { unsigned int p; cairo_surface_t *ss, *b; if (!ts->state.has_page && !ts->state.step) { ts->state.in_pages++; _cairo_transform_surface_recalc_state (ts); } switch (ts->state.type) { case CAIRO_TRANSFORM_SURFACE_STATE_IGNORE_PAGE: ts->state.has_page = 0; return NULL; case CAIRO_TRANSFORM_SURFACE_STATE_PROCESS_PAGE: if (ts->pages[1].num_elements) { if (ts->state.out_pages >= ts->pages[1].num_elements) { ts->state.has_page = 0; return NULL; } _cairo_array_copy_element (&ts->pages[1], ts->state.out_pages, &p); if (p + 1 != ts->state.in_pages) { ts->state.has_page = 0; return NULL; } } if (ts->state.step == ts->state.num_steps) { ts->state.step = 0; ts->state.out_pages++; ts->state.has_page = 0; _cairo_transform_surface_recalc_state (ts); return NULL; } if (!ts->state.step || !ts->buffer) { _cairo_array_copy_element (&ts->surfaces, ts->state.step++, &ss); return ss; } b = _cairo_transform_surface_get_meta (ts); for (; ts->state.step < ts->state.num_steps; ts->state.step++) { _cairo_array_copy_element (&ts->surfaces, ts->state.step, &ss); _cairo_meta_surface_replay (b, ss); _cairo_surface_show_page (ss); } ts->state.has_page = 0; ts->state.step = 0; return NULL; case CAIRO_TRANSFORM_SURFACE_STATE_BUFFER_PAGE: default: /* To silence the compiler */ ts->state.step = 0; ts->state.has_page = 0; return NULL; } } static cairo_surface_t * _cairo_transform_surface_get_surface (cairo_transform_surface_t *ts) { cairo_surface_t *ss = NULL; if (!ts->state.has_page) { ts->state.has_page = 1; if (!ts->state.step) { ts->state.in_pages++; _cairo_transform_surface_recalc_state (ts); } } switch (ts->state.type) { case CAIRO_TRANSFORM_SURFACE_STATE_IGNORE_PAGE: return NULL; case CAIRO_TRANSFORM_SURFACE_STATE_PROCESS_PAGE: if (ts->state.step == ts->state.num_steps) { ts->state.step = 0; return NULL; } if (!ts->state.step || !ts->buffer) { _cairo_array_copy_element (&ts->surfaces, ts->state.step++, &ss); return ss; } ss = _cairo_transform_surface_get_meta (ts); ts->state.step = ts->state.num_steps; return ss; case CAIRO_TRANSFORM_SURFACE_STATE_BUFFER_PAGE: default: /* To silence the compiler */ if (ts->state.step) { ts->state.step = 0; return NULL; } ts->state.step++; return _cairo_transform_surface_get_meta (ts); } } cairo_surface_t * cairo_transform_surface_create (void) { cairo_transform_surface_t *ts; if ((!(ts = malloc (sizeof (cairo_transform_surface_t))))) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; } memset (ts, 0, sizeof (cairo_transform_surface_t)); _cairo_surface_init (&ts->base, &cairo_transform_surface_backend); _cairo_array_init (&ts->surfaces, sizeof (cairo_transform_surface_t *)); _cairo_array_init (&ts->pages[0], sizeof (unsigned int)); _cairo_array_init (&ts->pages[1], sizeof (unsigned int)); _cairo_array_init (&ts->metas, sizeof (cairo_meta_surface_t *)); cairo_matrix_init_identity (&ts->matrix); return (cairo_surface_t *) ts; } static cairo_surface_t * _cairo_transform_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_transform_surface_t *ts, *tsn; cairo_surface_t *ss; cairo_status_t s; ts = (cairo_transform_surface_t *) abstract_surface; tsn = (cairo_transform_surface_t *) cairo_transform_surface_create (); if ((s = cairo_surface_status ((cairo_surface_t *) tsn))) return (cairo_surface_t *) tsn; while (!s && (tsn->surfaces.num_elements < ts->surfaces.num_elements)) { _cairo_array_copy_element (&ts->surfaces, tsn->surfaces.num_elements, &ss); s = cairo_transform_surface_insert_surface ((cairo_surface_t *) tsn, tsn->surfaces.num_elements, ss); } if (s) return (cairo_surface_t *) tsn; memcpy (&tsn->matrix, &ts->matrix, sizeof (cairo_matrix_t)); return (cairo_surface_t *) tsn; } static cairo_status_t _cairo_transform_surface_finish (void *abstract_surface) { cairo_transform_surface_t *ts = abstract_surface; unsigned int i; cairo_surface_t *ss; while (ts->surfaces.num_elements) cairo_transform_surface_remove_surface ((cairo_surface_t *) ts, 0); cairo_transform_surface_set_pages ((cairo_surface_t *) ts, NULL, 0, 0); cairo_transform_surface_set_pages ((cairo_surface_t *) ts, NULL, 0, 1); for (i = 0; i < ts->metas.num_elements; i++) { _cairo_array_copy_element (&ts->metas, i, &ss); if (ss) cairo_surface_destroy (ss); } _cairo_array_fini (&ts->surfaces); _cairo_array_fini (&ts->pages[0]); _cairo_array_fini (&ts->pages[1]); _cairo_array_fini (&ts->metas); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_transform_surface_show_page (void *abstract_surface) { cairo_transform_surface_t *ts = abstract_surface; cairo_surface_t *ss; cairo_status_t s = CAIRO_STATUS_SUCCESS; while (!s && (ss = _cairo_transform_surface_get_surface_show_page (ts))) s = _cairo_surface_show_page (ss); return s; } static cairo_status_t _cairo_transform_surface_flush (void *abstract_surface) { cairo_transform_surface_t *ts = abstract_surface; cairo_surface_t *ss; unsigned int i, p; cairo_status_t s = CAIRO_STATUS_SUCCESS; if (ts->state.out_pages < ts->pages[1].num_elements) { _cairo_array_copy_element (&ts->pages[1], ts->state.out_pages, &p); while (ts->state.in_pages < p + 1) while (!s && (ss = _cairo_transform_surface_get_surface_show_page (ts))) s = _cairo_surface_show_page (ss); } for (i = 0; !s && (i < ts->surfaces.num_elements); i++) { _cairo_array_copy_element (&ts->surfaces, i, &ss); cairo_surface_flush (ss); } return s; } static cairo_int_status_t _cairo_transform_surface_stroke (void *abstract_surface, cairo_operator_t op, cairo_pattern_t *src, cairo_path_fixed_t *path, cairo_stroke_style_t *style, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tol, cairo_antialias_t anti) { cairo_transform_surface_t *ts = abstract_surface; cairo_surface_t *ss; cairo_status_t s = CAIRO_STATUS_SUCCESS; cairo_matrix_t m, mi; cairo_path_fixed_t p; if ((!(ss = _cairo_transform_surface_get_surface (ts)))) return CAIRO_STATUS_SUCCESS; if ((s = _cairo_path_fixed_init_copy_transform (path, &p, &ts->matrix))) return s; cairo_matrix_multiply (&m, &ts->matrix, ctm); memcpy (&mi, &m, sizeof (cairo_matrix_t)); if ((s = cairo_matrix_invert (&mi))) return s; for ( ; ss && !s; ss = _cairo_transform_surface_get_surface (ts)) s = _cairo_surface_stroke (ss, op, src, &p, style, &m, &mi, tol, anti); _cairo_path_fixed_fini (&p); return s; } static void _cairo_trapezoid_transform (cairo_trapezoid_t *t, cairo_matrix_t *m) { double d[2]; d[0] = _cairo_fixed_to_double (t->left.p1.x); d[1] = _cairo_fixed_to_double (t->left.p1.y); cairo_matrix_transform_point (m, &d[0], &d[1]); t->left.p1.x = _cairo_fixed_from_double (d[0]); t->left.p1.y = _cairo_fixed_from_double (d[1]); d[0] = _cairo_fixed_to_double (t->left.p2.x); d[1] = _cairo_fixed_to_double (t->left.p2.y); cairo_matrix_transform_point (m, &d[0], &d[1]); t->left.p2.x = _cairo_fixed_from_double (d[0]); t->left.p2.y = _cairo_fixed_from_double (d[1]); d[0] = _cairo_fixed_to_double (t->right.p1.x); d[1] = _cairo_fixed_to_double (t->right.p1.y); cairo_matrix_transform_point (m, &d[0], &d[1]); t->right.p1.x = _cairo_fixed_from_double (d[0]); t->right.p1.y = _cairo_fixed_from_double (d[1]); d[0] = _cairo_fixed_to_double (t->right.p2.x); d[1] = _cairo_fixed_to_double (t->right.p2.y); cairo_matrix_transform_point (m, &d[0], &d[1]); t->right.p2.x = _cairo_fixed_from_double (d[0]); t->right.p2.y = _cairo_fixed_from_double (d[1]); } static cairo_int_status_t _cairo_transform_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t anti, int x_src, int y_src, int x_dst, int y_dst, unsigned int width, unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_transform_surface_t *ts = abstract_dst; cairo_surface_t *ss; unsigned int i; cairo_status_t s = CAIRO_STATUS_SUCCESS; cairo_trapezoid_t *t; if (!(ss = _cairo_transform_surface_get_surface (ts))) return CAIRO_STATUS_SUCCESS; if (!(t = malloc (sizeof (cairo_trapezoid_t) * num_traps))) return CAIRO_STATUS_NO_MEMORY; memcpy (t, traps, sizeof (cairo_trapezoid_t) * num_traps); for (i = 0; i < num_traps; i++) _cairo_trapezoid_transform (&(t[i]), &ts->matrix); for ( ; ss && !s; ss = _cairo_transform_surface_get_surface (ts)) s = _cairo_surface_composite_trapezoids (op, pattern, ss, anti, x_src, y_src, x_dst, y_dst, width, height, t, num_traps); free (t); return s; } static cairo_int_status_t _cairo_transform_surface_paint (void *abstract_surface, cairo_operator_t op, cairo_pattern_t *source) { cairo_transform_surface_t *ts = abstract_surface; cairo_surface_t *ss; cairo_status_t s = CAIRO_STATUS_SUCCESS; if (!(ss = _cairo_transform_surface_get_surface (ts))) return CAIRO_STATUS_SUCCESS; for ( ; ss && !s; ss = _cairo_transform_surface_get_surface (ts)) s = _cairo_surface_paint (ss, op, source); return s; } static cairo_int_status_t _cairo_transform_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, cairo_pattern_t *src, const cairo_glyph_t *g, int ng, cairo_scaled_font_t *sf) { cairo_transform_surface_t *ts = abstract_surface; cairo_surface_t *ss; cairo_status_t s = CAIRO_STATUS_SUCCESS; if (!(ss = _cairo_transform_surface_get_surface (ts))) return CAIRO_STATUS_SUCCESS; for ( ; ss && !s; ss = _cairo_transform_surface_get_surface (ts)) s = _cairo_surface_show_glyphs (ss, op, src, g, ng, sf); return s; } static cairo_int_status_t _cairo_transform_surface_fill (void *abstract_surface, cairo_operator_t op, cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tol, cairo_antialias_t anti) { cairo_transform_surface_t *ts = abstract_surface; cairo_surface_t *ss; cairo_status_t s = CAIRO_STATUS_SUCCESS; cairo_path_fixed_t p; if (!(ss = _cairo_transform_surface_get_surface (ts))) return CAIRO_STATUS_SUCCESS; if ((s = _cairo_path_fixed_init_copy_transform (path, &p, &ts->matrix))) return s; for ( ; ss && !s; ss = _cairo_transform_surface_get_surface (ts)) s = _cairo_surface_fill (ss, op, source, &p, fill_rule, tol, anti); _cairo_path_fixed_fini (&p); return s; } static const cairo_surface_backend_t cairo_transform_surface_backend = { _cairo_transform_surface_create_similar, _cairo_transform_surface_finish, NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* acquire_dest_image */ NULL, /* release_dest_image */ NULL, /* clone_similar */ NULL, /* composite */ NULL, /* fill_rectangles */ _cairo_transform_surface_composite_trapezoids, NULL, /* copy_page */ _cairo_transform_surface_show_page, NULL, /* set_clip_region */ NULL, /* intersect_clip_path */ NULL, /* get_extents */ NULL, /* old_show_glyphs */ NULL, /* get_font_options */ _cairo_transform_surface_flush, NULL, /* mark_dirty_rectangle */ NULL, /* scaled_font_fini */ NULL, /* scaled_glyph_fini */ _cairo_transform_surface_paint, NULL, /* mask */ _cairo_transform_surface_stroke, _cairo_transform_surface_fill, _cairo_transform_surface_show_glyphs }; static cairo_bool_t _cairo_surface_is_transform (cairo_surface_t *surface) { return surface->backend == &cairo_transform_surface_backend; } cairo_public cairo_status_t cairo_transform_surface_insert_surface (cairo_surface_t *surface, unsigned int pos, cairo_surface_t *surface_to_insert) { assert (_cairo_surface_is_transform (surface)); cairo_transform_surface_t *ts = (cairo_transform_surface_t *) surface; cairo_status_t s; assert (!ts->state.has_page); pos = MIN (pos, ts->surfaces.num_elements); s = _cairo_array_grow_by (&ts->surfaces, 1); if (s) return s; if (pos < ts->surfaces.num_elements) memmove (&(*ts->surfaces.elements)[(pos + 1) * ts->surfaces.element_size], &(*ts->surfaces.elements)[pos * ts->surfaces.element_size], ts->surfaces.element_size * (ts->surfaces.num_elements - pos)); memcpy (&(*ts->surfaces.elements)[pos * ts->surfaces.element_size], &surface_to_insert, ts->surfaces.element_size); ts->surfaces.num_elements++; cairo_surface_reference (surface_to_insert); memset (&ts->state, 0, sizeof (cairo_transform_surface_state_t)); _cairo_transform_surface_recalc_state (ts); return CAIRO_STATUS_SUCCESS; } cairo_public cairo_status_t cairo_transform_surface_remove_surface (cairo_surface_t *surface, unsigned int pos) { assert (_cairo_surface_is_transform (surface)); cairo_transform_surface_t *ts = (cairo_transform_surface_t *) surface; cairo_surface_t *sr; pos = MIN (MAX (pos, 0), ts->surfaces.num_elements); _cairo_array_copy_element (&ts->surfaces, pos, &sr); if (pos <= ts->surfaces.num_elements) memmove (&(*ts->surfaces.elements)[pos * ts->surfaces.element_size], &(*ts->surfaces.elements)[(pos + 1) * ts->surfaces.element_size], ts->surfaces.element_size * (ts->surfaces.num_elements - pos + 1)); _cairo_array_truncate (&ts->surfaces, ts->surfaces.num_elements - 1); cairo_surface_destroy (sr); memset (&ts->state, 0, sizeof (cairo_transform_surface_state_t)); _cairo_transform_surface_recalc_state (ts); return CAIRO_STATUS_SUCCESS; } cairo_public cairo_surface_t * cairo_transform_surface_get_surface (cairo_surface_t *surface, unsigned int pos) { assert (_cairo_surface_is_transform (surface)); cairo_transform_surface_t *ts = (cairo_transform_surface_t *) surface; cairo_surface_t *ss; if (pos >= ts->surfaces.num_elements) return NULL; _cairo_array_copy_element (&ts->surfaces, pos, &ss); return ss; } cairo_public void cairo_transform_surface_set_matrix (cairo_surface_t *surface, const cairo_matrix_t *matrix) { assert (_cairo_surface_is_transform (surface)); cairo_transform_surface_t *ts = (cairo_transform_surface_t *) surface; memcpy (&ts->matrix, matrix, sizeof (cairo_matrix_t)); } cairo_public cairo_status_t cairo_transform_surface_set_pages (cairo_surface_t *surface, unsigned int *pages, unsigned int num_pages, cairo_bool_t in) { assert (_cairo_surface_is_transform (surface)); cairo_transform_surface_t *ts = (cairo_transform_surface_t *) surface; cairo_status_t s = CAIRO_STATUS_SUCCESS; _cairo_array_truncate (&ts->pages[in & 1], 0); if (num_pages) s = _cairo_array_append_multiple (&ts->pages[in & 1], pages, num_pages); _cairo_transform_surface_recalc_state (ts); return s; } cairo_public void cairo_transform_surface_set_buffer (cairo_surface_t *surface, cairo_bool_t buffer) { assert (_cairo_surface_is_transform (surface)); cairo_transform_surface_t *ts = (cairo_transform_surface_t *) surface; ts->buffer = buffer; }