diff options
Diffstat (limited to 'src')
86 files changed, 11760 insertions, 30895 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3f76d2726..8c624f247 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,56 +1,55 @@ if CAIRO_HAS_PS_SURFACE libcairo_ps_headers = cairo-ps.h -libcairo_ps_sources = cairo_ps_surface.c +libcairo_ps_sources = cairo-ps-surface.c endif if CAIRO_HAS_PDF_SURFACE libcairo_pdf_headers = cairo-pdf.h -libcairo_pdf_sources = cairo_pdf_surface.c +libcairo_pdf_sources = cairo-pdf-surface.c endif -if CAIRO_HAS_PNG_SURFACE -libcairo_png_headers = cairo-png.h -libcairo_png_sources = cairo_png_surface.c +if CAIRO_HAS_PNG_FUNCTIONS +libcairo_png_sources = cairo-png.c endif if CAIRO_HAS_XLIB_SURFACE -libcairo_xlib_headers = cairo-xlib.h -libcairo_xlib_sources = cairo_xlib_surface.c +libcairo_xlib_headers = cairo-xlib.h cairo-xlib-xrender.h +libcairo_xlib_sources = cairo-xlib-surface.c cairo-xlib-test.h endif if CAIRO_HAS_QUARTZ_SURFACE libcairo_quartz_headers = cairo-quartz.h -libcairo_quartz_sources = cairo_quartz_surface.c +libcairo_quartz_sources = cairo-quartz-surface.c endif if CAIRO_HAS_XCB_SURFACE -libcairo_xcb_headers = cairo-xcb.h -libcairo_xcb_sources = cairo_xcb_surface.c +libcairo_xcb_headers = cairo-xcb.h cairo-xcb-xrender.h +libcairo_xcb_sources = cairo-xcb-surface.c endif libcairo_win32_sources = if CAIRO_HAS_WIN32_SURFACE libcairo_win32_headers = cairo-win32.h -libcairo_win32_sources += cairo_win32_surface.c cairo-win32-private.h +libcairo_win32_sources += cairo-win32-surface.c cairo-win32-private.h endif if CAIRO_HAS_WIN32_FONT -libcairo_win32_sources += cairo_win32_font.c +libcairo_win32_sources += cairo-win32-font.c endif if CAIRO_HAS_GLITZ_SURFACE libcairo_glitz_headers = cairo-glitz.h -libcairo_glitz_sources = cairo_glitz_surface.c +libcairo_glitz_sources = cairo-glitz-surface.c endif if CAIRO_HAS_ATSUI_FONT libcairo_atsui_headers = cairo-atsui.h -libcairo_atsui_sources = cairo_atsui_font.c +libcairo_atsui_sources = cairo-atsui-font.c endif if CAIRO_HAS_FT_FONT libcairo_ft_headers = cairo-ft.h -libcairo_ft_sources = cairo_ft_font.c cairo-ft-private.h +libcairo_ft_sources = cairo-ft-font.c cairo-ft-private.h endif # These names match automake style variable definition conventions so @@ -68,7 +67,6 @@ cairoinclude_HEADERS = \ $(libcairo_ft_headers) \ $(libcairo_glitz_headers) \ $(libcairo_pdf_headers) \ - $(libcairo_png_headers) \ $(libcairo_ps_headers) \ $(libcairo_quartz_headers) \ $(libcairo_win32_headers) \ @@ -80,28 +78,36 @@ lib_LTLIBRARIES = libcairo.la libcairo_la_SOURCES = \ cairo.c \ cairo.h \ - cairo_array.c \ - cairo_cache.c \ - cairo_color.c \ - cairo_fixed.c \ - cairo_font.c \ - cairo_gstate.c \ - cairo_hull.c \ - cairo_image_surface.c \ - cairo_matrix.c \ - cairo_path.c \ - cairo_path_bounds.c \ - cairo_path_fill.c \ - cairo_path_stroke.c \ - cairo_pen.c \ - cairo_polygon.c \ - cairo_slope.c \ - cairo_spline.c \ - cairo_surface.c \ - cairo_traps.c \ - cairo_pattern.c \ - cairo_unicode.c \ - cairo_wideint.c \ + cairo-private.h \ + cairo-arc.c \ + cairo-arc-private.h \ + cairo-array.c \ + cairo-cache.c \ + cairo-color.c \ + cairo-fixed.c \ + cairo-font.c \ + cairo-gstate.c \ + cairo-gstate-private.h \ + cairo-hull.c \ + cairo-image-surface.c \ + cairo-matrix.c \ + cairo-path.c \ + cairo-path-bounds.c \ + cairo-path-data.c \ + cairo-path-data-private.h \ + cairo-path-fill.c \ + cairo-path-fixed-private.h \ + cairo-path-stroke.c \ + cairo-pen.c \ + cairo-polygon.c \ + cairo-slope.c \ + cairo-spline.c \ + cairo-surface.c \ + cairo-traps.c \ + cairo-pattern.c \ + cairo-unicode.c \ + cairo-output-stream.c \ + cairo-wideint.c \ cairo-wideint.h \ $(libcairo_atsui_sources)\ $(libcairo_ft_sources)\ @@ -113,7 +119,6 @@ libcairo_la_SOURCES = \ $(libcairo_xcb_sources) \ $(libcairo_glitz_sources)\ $(libcairo_win32_sources)\ - $(libcairo_freetype_sources) \ cairoint.h libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined @@ -123,7 +128,7 @@ INCLUDES = -I$(srcdir) $(CAIRO_CFLAGS) libcairo_la_LIBADD = $(CAIRO_LIBS) install-data-local: - @if test -f $(includedir)/cairo.h || test -f $(includedir)/cairo-features.h ; then \ + @if test -f "$(DESTDIR)$(includedir)/cairo.h" || test -f "$(DESTDIR)$(includedir)/cairo-features.h" ; then \ echo "****************************************************************" ; \ echo "*** Error: Old headers found. You should remove the following" ; \ echo "*** files and then type 'make install' again." ; \ diff --git a/src/cairo-arc-private.h b/src/cairo-arc-private.h new file mode 100644 index 000000000..1cd41cc15 --- /dev/null +++ b/src/cairo-arc-private.h @@ -0,0 +1,57 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#ifndef CAIRO_ARC_PRIVATE_H +#define CAIRO_ARC_PRIVATE_H + +#include "cairoint.h" + +void +_cairo_arc_path (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2); + +void +_cairo_arc_path_negative (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2); + +#endif /* CAIRO_ARC_PRIVATE_H */ diff --git a/src/cairo-arc.c b/src/cairo-arc.c new file mode 100644 index 000000000..d3302cdd5 --- /dev/null +++ b/src/cairo-arc.c @@ -0,0 +1,296 @@ +/* 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. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth <cworth@cworth.org> + */ + +#include <math.h> + +#include "cairo-arc-private.h" + +/* Spline deviation from the circle in radius would be given by: + + error = sqrt (x**2 + y**2) - 1 + + A simpler error function to work with is: + + e = x**2 + y**2 - 1 + + From "Good approximation of circles by curvature-continuous Bezier + curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric + Design 8 (1990) 22-41, we learn: + + abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) + + and + abs (error) =~ 1/2 * e + + Of course, this error value applies only for the particular spline + approximation that is used in _cairo_gstate_arc_segment. +*/ +static double +_arc_error_normalized (double angle) +{ + return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); +} + +static double +_arc_max_angle_for_tolerance_normalized (double tolerance) +{ + double angle, error; + int i; + + /* Use table lookup to reduce search time in most cases. */ + struct { + double angle; + double error; + } table[] = { + { M_PI / 1.0, 0.0185185185185185036127 }, + { M_PI / 2.0, 0.000272567143730179811158 }, + { M_PI / 3.0, 2.38647043651461047433e-05 }, + { M_PI / 4.0, 4.2455377443222443279e-06 }, + { M_PI / 5.0, 1.11281001494389081528e-06 }, + { M_PI / 6.0, 3.72662000942734705475e-07 }, + { M_PI / 7.0, 1.47783685574284411325e-07 }, + { M_PI / 8.0, 6.63240432022601149057e-08 }, + { M_PI / 9.0, 3.2715520137536980553e-08 }, + { M_PI / 10.0, 1.73863223499021216974e-08 }, + { M_PI / 11.0, 9.81410988043554039085e-09 }, + }; + int table_size = (sizeof (table) / sizeof (table[0])); + + for (i = 0; i < table_size; i++) + if (table[i].error < tolerance) + return table[i].angle; + + ++i; + do { + angle = M_PI / i++; + error = _arc_error_normalized (angle); + } while (error > tolerance); + + return angle; +} + +static int +_arc_segments_needed (double angle, + double radius, + cairo_matrix_t *ctm, + double tolerance) +{ + double l1, l2, lmax; + double max_angle; + + _cairo_matrix_compute_eigen_values (ctm, &l1, &l2); + + l1 = fabs (l1); + l2 = fabs (l2); + if (l1 > l2) + lmax = l1; + else + lmax = l2; + + max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / (radius * lmax)); + + return (int) ceil (angle / max_angle); +} + +/* We want to draw a single spline approximating a circular arc radius + R from angle A to angle B. Since we want a symmetric spline that + matches the endpoints of the arc in position and slope, we know + that the spline control points must be: + + (R * cos(A), R * sin(A)) + (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) + (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) + (R * cos(B), R * sin(B)) + + for some value of h. + + "Approximation of circular arcs by cubic poynomials", Michael + Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides + various values of h along with error analysis for each. + + From that paper, a very practical value of h is: + + h = 4/3 * tan(angle/4) + + This value does not give the spline with minimal error, but it does + provide a very good approximation, (6th-order convergence), and the + error expression is quite simple, (see the comment for + _arc_error_normalized). +*/ +static void +_cairo_arc_segment (cairo_t *cr, + double xc, + double yc, + double radius, + double angle_A, + double angle_B) +{ + double r_sin_A, r_cos_A; + double r_sin_B, r_cos_B; + double h; + + r_sin_A = radius * sin (angle_A); + r_cos_A = radius * cos (angle_A); + r_sin_B = radius * sin (angle_B); + r_cos_B = radius * cos (angle_B); + + h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); + + cairo_curve_to (cr, + xc + r_cos_A - h * r_sin_A, + yc + r_sin_A + h * r_cos_A, + xc + r_cos_B + h * r_sin_B, + yc + r_sin_B - h * r_cos_B, + xc + r_cos_B, + yc + r_sin_B); +} + +static void +_cairo_arc_in_direction (cairo_t *cr, + double xc, + double yc, + double radius, + double angle_min, + double angle_max, + cairo_direction_t dir) +{ + while (angle_max - angle_min > 4 * M_PI) + angle_max -= 2 * M_PI; + + /* Recurse if drawing arc larger than pi */ + if (angle_max - angle_min > M_PI) { + double angle_mid = angle_min + (angle_max - angle_min) / 2.0; + /* XXX: Something tells me this block could be condensed. */ + if (dir == CAIRO_DIRECTION_FORWARD) { + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_min, angle_mid, + dir); + + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_mid, angle_max, + dir); + } else { + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_mid, angle_max, + dir); + + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_min, angle_mid, + dir); + } + } else { + cairo_matrix_t ctm; + int i, segments; + double angle, angle_step; + + cairo_get_matrix (cr, &ctm); + segments = _arc_segments_needed (angle_max - angle_min, + radius, &ctm, + cairo_get_tolerance (cr)); + angle_step = (angle_max - angle_min) / (double) segments; + + if (dir == CAIRO_DIRECTION_FORWARD) { + angle = angle_min; + } else { + angle = angle_max; + angle_step = - angle_step; + } + + for (i = 0; i < segments; i++, angle += angle_step) { + _cairo_arc_segment (cr, xc, yc, + radius, + angle, + angle + angle_step); + } + } +} + +/** + * _cairo_arc_path_negative: + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Compute a path for the given arc and append it onto the current + * path within @cr. The arc will be accurate within the current + * tolerance and given the current transformation. + **/ +void +_cairo_arc_path (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2) +{ + _cairo_arc_in_direction (cr, xc, yc, + radius, + angle1, angle2, + CAIRO_DIRECTION_FORWARD); +} + +/** + * _cairo_arc_path_negative: + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * @ctm: the current transformation matrix + * @tolerance: the current tolerance value + * @path: the path onto which th earc will be appended + * + * Compute a path for the given arc (defined in the negative + * direction) and append it onto the current path within @cr. The arc + * will be accurate within the current tolerance and given the current + * transformation. + **/ +void +_cairo_arc_path_negative (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2) +{ + _cairo_arc_in_direction (cr, xc, yc, + radius, + angle2, angle1, + CAIRO_DIRECTION_REVERSE); +} diff --git a/src/cairo-array.c b/src/cairo-array.c index 2b1cf9d61..a37ea9af5 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -132,3 +132,142 @@ _cairo_array_num_elements (cairo_array_t *array) { return array->num_elements; } + +/* cairo_user_data_array_t */ + +typedef struct { + const cairo_user_data_key_t *key; + void *user_data; + cairo_destroy_func_t destroy; +} cairo_user_data_slot_t; + +/** + * _cairo_user_data_array_init: + * @array: a #cairo_user_data_array_t + * + * Initializes a #cairo_user_data_array_t structure for future + * use. After initialization, the array has no keys. Call + * _cairo_user_data_array_destroy() to free any allocated memory + * when done using the array. + **/ +void +_cairo_user_data_array_init (cairo_user_data_array_t *array) +{ + _cairo_array_init (array, sizeof (cairo_user_data_slot_t)); +} + +/** + * _cairo_user_data_array_destroy: + * @array: a #cairo_user_data_array_t + * + * Destroys all current keys in the user data array and deallocates + * any memory allocated for the array itself. + **/ +void +_cairo_user_data_array_destroy (cairo_user_data_array_t *array) +{ + int i, num_slots; + cairo_user_data_slot_t *slots; + + num_slots = array->num_elements; + slots = (cairo_user_data_slot_t *) array->elements; + for (i = 0; i < num_slots; i++) { + if (slots[i].user_data != NULL && slots[i].destroy != NULL) + slots[i].destroy (slots[i].user_data); + } + + _cairo_array_fini (array); +} + +/** + * _cairo_user_data_array_get_data: + * @array: a #cairo_user_data_array_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Returns user data previously attached using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +_cairo_user_data_array_get_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key) +{ + int i, num_slots; + cairo_user_data_slot_t *slots; + + num_slots = array->num_elements; + slots = (cairo_user_data_slot_t *) array->elements; + for (i = 0; i < num_slots; i++) { + if (slots[i].key == key) + return slots[i].user_data; + } + + return NULL; +} + +/** + * _cairo_user_data_array_set_data: + * @array: a #cairo_user_data_array_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach + * @destroy: a #cairo_destroy_func_t which will be called when the + * user data array is destroyed or when new user data is attached using the + * same key. + * + * Attaches user data to a user data array. To remove user data, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +_cairo_user_data_array_set_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + int i, num_slots; + cairo_user_data_slot_t *slots, *s; + + s = NULL; + num_slots = array->num_elements; + slots = (cairo_user_data_slot_t *) array->elements; + for (i = 0; i < num_slots; i++) { + if (slots[i].key == key) { + if (slots[i].user_data != NULL && slots[i].destroy != NULL) + slots[i].destroy (slots[i].user_data); + s = &slots[i]; + break; + } + if (user_data && slots[i].user_data == NULL) { + s = &slots[i]; /* Have to keep searching for an exact match */ + } + } + + if (user_data == NULL) { + if (s != NULL) { + s->key = NULL; + s->user_data = NULL; + s->destroy = NULL; + } + + return CAIRO_STATUS_SUCCESS; + + } else { + if (s == NULL) + s = _cairo_array_append (array, NULL, 1); + if (s == NULL) + return CAIRO_STATUS_NO_MEMORY; + + s->key = key; + s->user_data = user_data; + s->destroy = destroy; + } + + return CAIRO_STATUS_SUCCESS; +} + diff --git a/src/cairo-atsui-font.c b/src/cairo-atsui-font.c index cb4b1c5d7..a279956df 100644 --- a/src/cairo-atsui-font.c +++ b/src/cairo-atsui-font.c @@ -30,778 +30,661 @@ * The Initial Developer of the Original Code is Calum Robinson * * Contributor(s): - * Calum Robinson <calumr@mac.com> + * Calum Robinson <calumr@mac.com> */ #include <stdlib.h> #include <math.h> - #include "cairo-atsui.h" #include "cairoint.h" +#include "cairo.h" +#include <iconv.h> +typedef struct { + cairo_scaled_font_t base; + cairo_matrix_t scale; + ATSUStyle style; + ATSUStyle unscaled_style; + ATSUFontID fontID; +} cairo_atsui_font_t; +typedef struct cairo_ATSUI_glyph_path_callback_info_t { + cairo_path_fixed_t *path; + cairo_matrix_t scale; +} cairo_ATSUI_glyph_path_callback_info_t; +const cairo_scaled_font_backend_t cairo_atsui_scaled_font_backend; -#pragma mark Types +static CGAffineTransform +CGAffineTransformMakeWithCairoFontScale(cairo_matrix_t *scale) +{ + return CGAffineTransformMake(scale->xx, scale->yx, + scale->xy, scale->yy, + 0, 0); +} +static ATSUStyle +CreateSizedCopyOfStyle(ATSUStyle inStyle, cairo_matrix_t *scale) +{ + ATSUStyle style; + OSStatus err; -typedef struct { - cairo_unscaled_font_t base; - - ATSUStyle style; - ATSUFontID fontID; -} cairo_atsui_font_t; + // Set the style's size + CGAffineTransform theTransform = + CGAffineTransformMakeWithCairoFontScale(scale); + Fixed theSize = + FloatToFixed(CGSizeApplyAffineTransform + (CGSizeMake(1.0, 1.0), theTransform).height); + const ATSUAttributeTag theFontStyleTags[] = { kATSUSizeTag }; + const ByteCount theFontStyleSizes[] = { sizeof(Fixed) }; + ATSUAttributeValuePtr theFontStyleValues[] = { &theSize }; + err = ATSUCreateAndCopyStyle(inStyle, &style); -typedef struct cairo_ATSUI_glyph_path_callback_info_t { - cairo_path_t *path; - cairo_matrix_t scale; -} cairo_ATSUI_glyph_path_callback_info_t; + err = ATSUSetAttributes(style, + sizeof(theFontStyleTags) / + sizeof(ATSUAttributeTag), theFontStyleTags, + theFontStyleSizes, theFontStyleValues); + return style; +} +static cairo_status_t +_cairo_atsui_font_create(const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **font_out) +{ + cairo_atsui_font_t *font = NULL; + ATSUStyle style; + ATSUFontID fontID; + OSStatus err; + Boolean isItalic, isBold; + cairo_matrix_t scale; + + err = ATSUCreateStyle(&style); + + + switch (weight) { + case CAIRO_FONT_WEIGHT_BOLD: + isBold = true; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + isBold = false; + break; + } + + switch (slant) { + case CAIRO_FONT_SLANT_ITALIC: + isItalic = true; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + isItalic = false; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + isItalic = false; + break; + } + + err = ATSUFindFontFromName(family, strlen(family), + kFontFamilyName, + kFontNoPlatformCode, + kFontRomanScript, + kFontNoLanguageCode, &fontID); + + if (err != noErr) { + // couldn't get the font - remap css names and try again + + if (!strcmp(family, "serif")) + family = "Times"; + else if (!strcmp(family, "sans-serif")) + family = "Helvetica"; + else if (!strcmp(family, "cursive")) + family = "Apple Chancery"; + else if (!strcmp(family, "fantasy")) + family = "Gadget"; + else if (!strcmp(family, "monospace")) + family = "Courier"; + else // anything else - return error instead? + family = "Courier"; + + err = ATSUFindFontFromName(family, strlen(family), + kFontFamilyName, + kFontNoPlatformCode, + kFontRomanScript, + kFontNoLanguageCode, &fontID); + } + + + ATSUAttributeTag styleTags[] = + { kATSUQDItalicTag, kATSUQDBoldfaceTag, kATSUFontTag }; + ATSUAttributeValuePtr styleValues[] = { &isItalic, &isBold, &fontID }; + ByteCount styleSizes[] = + { sizeof(Boolean), sizeof(Boolean), sizeof(ATSUFontID) }; + + + err = ATSUSetAttributes(style, + sizeof(styleTags) / sizeof(styleTags[0]), + styleTags, styleSizes, styleValues); + + font = malloc(sizeof(cairo_atsui_font_t)); + + _cairo_scaled_font_init(&font->base, font_matrix, ctm, + &cairo_atsui_scaled_font_backend); + + cairo_matrix_multiply(&scale, font_matrix, ctm); + font->style = CreateSizedCopyOfStyle(style, &scale); + + Fixed theSize = FloatToFixed(1.0); + const ATSUAttributeTag theFontStyleTags[] = { kATSUSizeTag }; + const ByteCount theFontStyleSizes[] = { sizeof(Fixed) }; + ATSUAttributeValuePtr theFontStyleValues[] = { &theSize }; + err = ATSUSetAttributes(style, + sizeof(theFontStyleTags) / + sizeof(ATSUAttributeTag), theFontStyleTags, + theFontStyleSizes, theFontStyleValues); + + font->unscaled_style = style; + + font->fontID = fontID; + font->scale = scale; + + *font_out = &font->base; + + return CAIRO_STATUS_SUCCESS; +} -#pragma mark Private Functions +static void +_cairo_atsui_font_destroy_font(void *abstract_font) +{ + cairo_atsui_font_t *font = abstract_font; + if (font == NULL) + return; + if (font->style) + ATSUDisposeStyle(font->style); + if (font->unscaled_style) + ATSUDisposeStyle(font->unscaled_style); +} -static CGAffineTransform CGAffineTransformMakeWithCairoFontScale(cairo_font_scale_t scale) +static void +_cairo_atsui_font_get_glyph_cache_key(void *abstract_font, + cairo_glyph_cache_key_t *key) { - return CGAffineTransformMake( scale.matrix[0][0], scale.matrix[0][1], - scale.matrix[1][0], scale.matrix[1][1], - 0, 0); } -static ATSUStyle CreateSizedCopyOfStyle(ATSUStyle inStyle, cairo_font_scale_t *scale) +static cairo_status_t +_cairo_atsui_font_text_to_glyphs(void *abstract_font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - ATSUStyle style; - OSStatus err; - - - // Set the style's size - CGAffineTransform theTransform = CGAffineTransformMakeWithCairoFontScale(*scale); - Fixed theSize = FloatToFixed(CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), theTransform).height); - const ATSUAttributeTag theFontStyleTags[] = { kATSUSizeTag }; - const ByteCount theFontStyleSizes[] = { sizeof(Fixed) }; - ATSUAttributeValuePtr theFontStyleValues[] = { &theSize }; - - err = ATSUCreateAndCopyStyle(inStyle, &style); - - err = ATSUSetAttributes( style, - sizeof(theFontStyleTags) / sizeof(ATSUAttributeTag), - theFontStyleTags, theFontStyleSizes, theFontStyleValues); - - - return style; -} + cairo_atsui_font_t *font = abstract_font; + size_t i; + OSStatus err; + ATSUTextLayout textLayout; + ATSLayoutRecord *layoutRecords; + ItemCount glyphCount, charCount; + UniChar *theText; + err = ATSUCreateTextLayout(&textLayout); +#if 1 + charCount = strlen(utf8); + // Set the text in the text layout object, so we can measure it + theText = (UniChar *) malloc(charCount * sizeof(UniChar)); + for (i = 0; i < charCount; i++) { + theText[i] = utf8[i]; + } +#endif -#pragma mark Public Functions +#if 0 + // Set the text in the text layout object, so we can measure it + charCount = strlen(utf8); + theText = (UniChar *) malloc(charCount * sizeof(UniChar)); + size_t inBytes = charCount, outBytes = charCount; + iconv_t converter = iconv_open("UTF-8", "UTF-16"); + charCount = iconv(converter, utf8, &inBytes, theText, &outBytes); +#endif + err = ATSUSetTextPointerLocation(textLayout, + theText, 0, charCount, charCount); + // Set the style for all of the text + err = ATSUSetRunStyle(textLayout, + font->unscaled_style, kATSUFromTextBeginning, kATSUToTextEnd); -static cairo_unscaled_font_t * -_cairo_atsui_font_create( const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_atsui_font_t *font = NULL; - ATSUStyle style; - ATSUFontID fontID; - OSStatus err; - Boolean isItalic, isBold; - - - err = ATSUCreateStyle(&style); - - - switch (weight) - { - case CAIRO_FONT_WEIGHT_BOLD: - isBold = true; - break; - case CAIRO_FONT_WEIGHT_NORMAL: - default: - isBold = false; - break; - } - - switch (slant) - { - case CAIRO_FONT_SLANT_ITALIC: - isItalic = true; - break; - case CAIRO_FONT_SLANT_OBLIQUE: - isItalic = false; - break; - case CAIRO_FONT_SLANT_NORMAL: - default: - isItalic = false; - break; - } - - err = ATSUFindFontFromName( family, strlen(family), - kFontFamilyName, - kFontNoPlatformCode, - kFontRomanScript, - kFontNoLanguageCode, - &fontID); - - - ATSUAttributeTag styleTags[] = {kATSUQDItalicTag, kATSUQDBoldfaceTag, kATSUFontTag}; - ATSUAttributeValuePtr styleValues[] = {&isItalic, &isBold, &fontID}; - ByteCount styleSizes[] = {sizeof(Boolean), sizeof(Boolean), sizeof(ATSUFontID)}; - - - err = ATSUSetAttributes( style, - sizeof(styleTags) / sizeof(styleTags[0]), - styleTags, - styleSizes, - styleValues); - - - - font = malloc(sizeof(cairo_atsui_font_t)); - - if (_cairo_unscaled_font_init(&font->base, &cairo_atsui_font_backend) == CAIRO_STATUS_SUCCESS) - { - font->style = style; - font->fontID = fontID; - - - return &font->base; - } - - - free(font); - - return NULL; -} + // Get the glyphs from the text layout object + err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(textLayout, + 0, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void *) + &layoutRecords, + &glyphCount); + *num_glyphs = glyphCount - 1; -static void -_cairo_atsui_font_destroy(void *abstract_font) -{ - cairo_atsui_font_t *font = abstract_font; - - - if (font == NULL) - return; - - if (font->style) - ATSUDisposeStyle(font->style); - - free(font); -} + *glyphs = + (cairo_glyph_t *) malloc(*num_glyphs * (sizeof(cairo_glyph_t))); + if (*glyphs == NULL) { + return CAIRO_STATUS_NO_MEMORY; + } -static cairo_status_t -_cairo_atsui_font_text_to_glyphs( void *abstract_font, - cairo_font_scale_t *sc, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *nglyphs) -{ - cairo_atsui_font_t *font = abstract_font; - size_t i; - OSStatus err; - ATSUTextLayout textLayout; - ATSLayoutRecord *layoutRecords; - ItemCount glyphCount, charCount; - UniChar *theText; - ATSUStyle style; - - - charCount = strlen(utf8); - - - err = ATSUCreateTextLayout(&textLayout); - - - // Set the text in the text layout object, so we can measure it - theText = (UniChar *)malloc(charCount * sizeof(UniChar)); - - for (i = 0; i < charCount; i++) - { - theText[i] = utf8[i]; - } - - err = ATSUSetTextPointerLocation( textLayout, - theText, - 0, - charCount, - charCount); - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - // Set the style for all of the text - err = ATSUSetRunStyle( textLayout, - style, - kATSUFromTextBeginning, - kATSUToTextEnd); - - - - // Get the glyphs from the text layout object - err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( textLayout, - 0, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void *)&layoutRecords, - &glyphCount); - - *nglyphs = glyphCount; - - - *glyphs = (cairo_glyph_t *)malloc(glyphCount * (sizeof(cairo_glyph_t))); - if (*glyphs == NULL) - { - return CAIRO_STATUS_NO_MEMORY; - } - - for (i = 0; i < glyphCount; i++) - { - (*glyphs)[i].index = layoutRecords[i].glyphID; - (*glyphs)[i].x = FixedToFloat(layoutRecords[i].realPos); - (*glyphs)[i].y = 0; - } - - - free(theText); - - ATSUDirectReleaseLayoutDataArrayPtr( NULL, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void *)&layoutRecords); - - ATSUDisposeTextLayout(textLayout); - - ATSUDisposeStyle(style); - - - return CAIRO_STATUS_SUCCESS; + for (i = 0; i < *num_glyphs; i++) { + (*glyphs)[i].index = layoutRecords[i].glyphID; + (*glyphs)[i].x = FixedToFloat(layoutRecords[i].realPos); + (*glyphs)[i].y = 0; + } + + + free(theText); + + ATSUDirectReleaseLayoutDataArrayPtr(NULL, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void *) &layoutRecords); + + ATSUDisposeTextLayout(textLayout); + + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_atsui_font_font_extents( void *abstract_font, - cairo_font_scale_t *sc, - cairo_font_extents_t *extents) +static cairo_status_t +_cairo_atsui_font_font_extents(void *abstract_font, + cairo_font_extents_t * extents) { - cairo_atsui_font_t *font = abstract_font; - ATSFontRef atsFont; - ATSFontMetrics metrics; - OSStatus err; - - - // TODO - test this - - atsFont = FMGetATSFontRefFromFont(font->fontID); - - if (atsFont) - { - err = ATSFontGetHorizontalMetrics(atsFont, kATSOptionFlagsDefault, &metrics); - - if (err == noErr) - { - extents->ascent = metrics.ascent; - extents->descent = metrics.descent; - extents->height = metrics.capHeight; - extents->max_x_advance = metrics.maxAdvanceWidth; - - // The FT backend doesn't handle max_y_advance either, so we'll ignore it for now. - extents->max_y_advance = 0.0; - - - return CAIRO_STATUS_SUCCESS; - } - } - - - return CAIRO_STATUS_NULL_POINTER; + cairo_atsui_font_t *font = abstract_font; + ATSFontRef atsFont; + ATSFontMetrics metrics; + OSStatus err; + + // TODO - test this + + atsFont = FMGetATSFontRefFromFont(font->fontID); + + if (atsFont) { + err = + ATSFontGetHorizontalMetrics(atsFont, kATSOptionFlagsDefault, + &metrics); + + if (err == noErr) { + extents->ascent = metrics.ascent; + extents->descent = metrics.descent; + extents->height = metrics.capHeight; + extents->max_x_advance = metrics.maxAdvanceWidth; + + // The FT backend doesn't handle max_y_advance either, so we'll ignore it for now. + extents->max_y_advance = 0.0; + + return CAIRO_STATUS_SUCCESS; + } + } + + + return CAIRO_STATUS_NULL_POINTER; } -static cairo_status_t -_cairo_atsui_font_glyph_extents( void *abstract_font, - cairo_font_scale_t *sc, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +static cairo_status_t +_cairo_atsui_font_glyph_extents(void *abstract_font, + cairo_glyph_t * glyphs, + int num_glyphs, + cairo_text_extents_t * extents) { - cairo_atsui_font_t *font = abstract_font; - cairo_point_double_t origin; - cairo_point_double_t glyph_min, glyph_max; - cairo_point_double_t total_min, total_max; - OSStatus err; - ATSUStyle style; - int i; - - - if (num_glyphs == 0) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return CAIRO_STATUS_SUCCESS; - } - - origin.x = glyphs[0].x; - origin.y = glyphs[0].y; - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - for (i = 0; i < num_glyphs; i++) - { - GlyphID theGlyph = glyphs[i].index; - double minX, maxX, ascent, descent; - ATSGlyphIdealMetrics metricsH, metricsV; - - - err = ATSUGlyphGetIdealMetrics( style, - 1, - &theGlyph, - 0, - &metricsH); - - - ATSUVerticalCharacterType verticalType = kATSUStronglyVertical; - ATSUAttributeTag theTag = kATSUVerticalCharacterTag; - ByteCount theSize = sizeof(ATSUVerticalCharacterType); - - err = ATSUSetAttributes(style, 1, &theTag, &theSize, (ATSUAttributeValuePtr)&verticalType); - - err = ATSUGlyphGetIdealMetrics( style, - 1, - &theGlyph, - 0, - &metricsV); - - minX = metricsH.otherSideBearing.x; - maxX = metricsH.advance.x; - - ascent = metricsV.advance.x; - descent = metricsV.otherSideBearing.x; - - glyph_min.x = glyphs[i].x + minX; - glyph_min.y = glyphs[i].y + descent; - glyph_max.x = glyphs[i].x + maxX; - glyph_max.y = glyphs[i].y + ascent; - - if (i==0) - { - total_min = glyph_min; - total_max = glyph_max; - } - else - { - if (glyph_min.x < total_min.x) - total_min.x = glyph_min.x; - if (glyph_min.y < total_min.y) - total_min.y = glyph_min.y; - - if (glyph_max.x > total_max.x) - total_max.x = glyph_max.x; - if (glyph_max.y > total_max.y) - total_max.y = glyph_max.y; - } - } - - - extents->x_bearing = total_min.x - origin.x; - extents->y_bearing = total_min.y - origin.y; - extents->width = total_max.x - total_min.x; - extents->height = total_max.y - total_min.y; - extents->x_advance = glyphs[i-1].x - origin.x; - extents->y_advance = glyphs[i-1].y - origin.y; - - - return CAIRO_STATUS_SUCCESS; + cairo_atsui_font_t *font = abstract_font; + OSStatus err; + + assert(num_glyphs == 1); + + GlyphID theGlyph = glyphs[0].index; + + ATSGlyphIdealMetrics metricsH, metricsV; + ATSUStyle style; + + ATSUCreateAndCopyStyle(font->unscaled_style, &style); + + err = ATSUGlyphGetIdealMetrics(style, + 1, &theGlyph, 0, &metricsH); + + ATSUVerticalCharacterType verticalType = kATSUStronglyVertical; + const ATSUAttributeTag theTag[] = { kATSUVerticalCharacterTag }; + const ByteCount theSizes[] = { sizeof(verticalType) }; + ATSUAttributeValuePtr theValues[] = { &verticalType }; + + err = ATSUSetAttributes(style, 1, theTag, theSizes, theValues); + + err = ATSUGlyphGetIdealMetrics(style, + 1, &theGlyph, 0, &metricsV); + + extents->x_bearing = metricsH.sideBearing.x; + extents->y_bearing = metricsV.advance.y; + extents->width = + metricsH.advance.x - metricsH.sideBearing.x - metricsH.otherSideBearing.x; + extents->height = + -metricsV.advance.y - metricsV.sideBearing.y - metricsV.otherSideBearing.y; + extents->x_advance = metricsH.advance.x; + extents->y_advance = 0; + + ATSUDisposeStyle(style); + + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_atsui_font_glyph_bbox( void *abstract_font, - cairo_font_scale_t *sc, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +static cairo_status_t +_cairo_atsui_font_glyph_bbox(void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, cairo_box_t *bbox) { - cairo_atsui_font_t *font = abstract_font; - cairo_fixed_t x1, y1, x2, y2; - int i; - OSStatus err; - ATSUStyle style; - - - bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; - bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - for (i = 0; i < num_glyphs; i++) - { - GlyphID theGlyph = glyphs[i].index; - ATSGlyphIdealMetrics metrics; - - - err = ATSUGlyphGetIdealMetrics( style, - 1, - &theGlyph, - 0, - &metrics); - - x1 = _cairo_fixed_from_double(glyphs[i].x); - y1 = _cairo_fixed_from_double(glyphs[i].y); - x2 = x1 + _cairo_fixed_from_double(metrics.advance.x); - y2 = y1 + _cairo_fixed_from_double(metrics.advance.y); - - if (x1 < bbox->p1.x) - bbox->p1.x = x1; - - if (y1 < bbox->p1.y) - bbox->p1.y = y1; - - if (x2 > bbox->p2.x) - bbox->p2.x = x2; - - if (y2 > bbox->p2.y) - bbox->p2.y = y2; - } - - - ATSUDisposeStyle(style); - - - return CAIRO_STATUS_SUCCESS; + cairo_atsui_font_t *font = abstract_font; + cairo_fixed_t x1, y1, x2, y2; + int i; + + bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; + bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; + + + for (i = 0; i < num_glyphs; i++) { + GlyphID theGlyph = glyphs[i].index; + + ATSGlyphScreenMetrics metrics; + ATSUGlyphGetScreenMetrics(font->style, + 1, &theGlyph, 0, true, true, &metrics); + + x1 = _cairo_fixed_from_double(glyphs[i].x + metrics.topLeft.x); + y1 = _cairo_fixed_from_double(glyphs[i].y - metrics.topLeft.y); + x2 = x1 + _cairo_fixed_from_double(metrics.height); + y2 = y1 + _cairo_fixed_from_double(metrics.width); + + if (x1 < bbox->p1.x) + bbox->p1.x = x1; + + if (y1 < bbox->p1.y) + bbox->p1.y = y1; + + if (x2 > bbox->p2.x) + bbox->p2.x = x2; + + if (y2 > bbox->p2.y) + bbox->p2.y = y2; + } + + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_atsui_font_show_glyphs( void *abstract_font, - cairo_font_scale_t *sc, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - int num_glyphs) +static cairo_status_t +_cairo_atsui_font_show_glyphs(void *abstract_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) { - cairo_atsui_font_t *font = abstract_font; - CGContextRef myBitmapContext; - CGColorSpaceRef colorSpace; - cairo_image_surface_t *destImageSurface; - int i; - - - destImageSurface = _cairo_surface_get_image(surface); - - - // Create a CGBitmapContext for the dest surface for drawing into + cairo_atsui_font_t *font = abstract_font; + CGContextRef myBitmapContext; + CGColorSpaceRef colorSpace; + cairo_image_surface_t *destImageSurface; + int i; + + cairo_rectangle_t rect = {dest_x, dest_y, width, height}; + _cairo_surface_acquire_dest_image(generic_surface, + &rect, + &destImageSurface, + &rect, + NULL); + + // Create a CGBitmapContext for the dest surface for drawing into colorSpace = CGColorSpaceCreateDeviceRGB(); - - myBitmapContext = CGBitmapContextCreate( destImageSurface->data, - destImageSurface->width, - destImageSurface->height, - destImageSurface->depth / 4, - destImageSurface->stride, - colorSpace, - kCGImageAlphaPremultipliedFirst); - - - ATSFontRef atsFont = FMGetATSFontRefFromFont(font->fontID); - CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFont); - - CGContextSetFont(myBitmapContext, cgFont); - - - CGAffineTransform textTransform = CGAffineTransformMakeWithCairoFontScale(*sc); - CGSize textSize = CGSizeMake(1.0, 1.0); - - textSize = CGSizeApplyAffineTransform(textSize, textTransform); - - CGContextSetFontSize(myBitmapContext, textSize.width); - - - // TODO - bold and italic text - // - // We could draw the text using ATSUI and get bold, italics - // etc. for free, but ATSUI does a lot of text layout work - // that we don't really need... - - - for (i = 0; i < num_glyphs; i++) - { - CGGlyph theGlyph = glyphs[i].index; - - CGContextShowGlyphsAtPoint(myBitmapContext, source_x + glyphs[i].x, destImageSurface->height - (source_y + glyphs[i].y), &theGlyph, 1); - } - - - CGColorSpaceRelease(colorSpace); - CGContextRelease(myBitmapContext); - - - return CAIRO_STATUS_SUCCESS; -} + myBitmapContext = CGBitmapContextCreate(destImageSurface->data, + destImageSurface->width, + destImageSurface->height, + destImageSurface->depth / 4, + destImageSurface->stride, + colorSpace, + kCGImageAlphaPremultipliedFirst); + CGContextTranslateCTM(myBitmapContext, 0, destImageSurface->height); + CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f); + + ATSFontRef atsFont = FMGetATSFontRefFromFont(font->fontID); + CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFont); + + CGContextSetFont(myBitmapContext, cgFont); + + CGAffineTransform textTransform = + CGAffineTransformMakeWithCairoFontScale(&font->scale); + + textTransform = CGAffineTransformScale(textTransform, 1.0f, -1.0f); + + CGContextSetFontSize(myBitmapContext, 1.0); + CGContextSetTextMatrix(myBitmapContext, textTransform); + + if (pattern->type == CAIRO_PATTERN_SOLID && + _cairo_pattern_is_opaque_solid(pattern)) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *)pattern; + CGContextSetRGBFillColor(myBitmapContext, + solid->color.red, + solid->color.green, + solid->color.blue, 1.0f); + } else + CGContextSetRGBFillColor(myBitmapContext, 0.0f, 0.0f, 0.0f, 0.0f); + + // TODO - bold and italic text + // + // We could draw the text using ATSUI and get bold, italics + // etc. for free, but ATSUI does a lot of text layout work + // that we don't really need... + + + for (i = 0; i < num_glyphs; i++) { + CGGlyph theGlyph = glyphs[i].index; + + CGContextShowGlyphsAtPoint(myBitmapContext, + glyphs[i].x, + glyphs[i].y, + &theGlyph, 1); + } + + + CGColorSpaceRelease(colorSpace); + CGContextRelease(myBitmapContext); -#pragma mark - + _cairo_surface_release_dest_image(generic_surface, + &rect, + destImageSurface, + &rect, + NULL); + + return CAIRO_STATUS_SUCCESS; +} -static OSStatus MyATSCubicMoveToCallback(const Float32Point *pt, void *callBackDataPtr) +static OSStatus MyATSCubicMoveToCallback(const Float32Point * pt, + void *callBackDataPtr) { - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - double scaledPt[2]; - cairo_point_t point; - - - scaledPt[0] = pt->x; - scaledPt[1] = pt->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - point.x = _cairo_fixed_from_double(scaledPt[0]); - point.y = _cairo_fixed_from_double(scaledPt[1]); - - _cairo_path_move_to(info->path, &point); - - + cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; + double scaledPt[2]; + cairo_fixed_t x, y; + + scaledPt[0] = pt->x; + scaledPt[1] = pt->y; + + cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); + + x = _cairo_fixed_from_double(scaledPt[0]); + y = _cairo_fixed_from_double(scaledPt[1]); + + _cairo_path_fixed_close_path(info->path); + _cairo_path_fixed_move_to(info->path, x, y); + return noErr; } -static OSStatus MyATSCubicLineToCallback(const Float32Point *pt, void *callBackDataPtr) +static OSStatus MyATSCubicLineToCallback(const Float32Point * pt, + void *callBackDataPtr) { - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - cairo_point_t point; - double scaledPt[2]; - - - scaledPt[0] = pt->x; - scaledPt[1] = pt->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - point.x = _cairo_fixed_from_double(scaledPt[0]); - point.y = _cairo_fixed_from_double(scaledPt[1]); - - _cairo_path_line_to(info->path, &point); - - - return noErr; + cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; + double scaledPt[2]; + cairo_fixed_t x, y; + + scaledPt[0] = pt->x; + scaledPt[1] = pt->y; + + cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); + + x = _cairo_fixed_from_double(scaledPt[0]); + y = _cairo_fixed_from_double(scaledPt[1]); + + _cairo_path_fixed_line_to(info->path, x, y); + + + return noErr; } -static OSStatus MyATSCubicCurveToCallback( const Float32Point *pt1, - const Float32Point *pt2, - const Float32Point *pt3, - void *callBackDataPtr) +static OSStatus MyATSCubicCurveToCallback(const Float32Point * pt1, + const Float32Point * pt2, + const Float32Point * pt3, + void *callBackDataPtr) { - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - cairo_point_t p0, p1, p2; - double scaledPt[2]; - - - scaledPt[0] = pt1->x; - scaledPt[1] = pt1->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - p0.x = _cairo_fixed_from_double(scaledPt[0]); - p0.y = _cairo_fixed_from_double(scaledPt[1]); - + cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; + double scaledPt[2]; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + + + scaledPt[0] = pt1->x; + scaledPt[1] = pt1->y; + + cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); + + x0 = _cairo_fixed_from_double(scaledPt[0]); + y0 = _cairo_fixed_from_double(scaledPt[1]); + scaledPt[0] = pt2->x; - scaledPt[1] = pt2->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); + scaledPt[1] = pt2->y; + + cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); + + x1 = _cairo_fixed_from_double(scaledPt[0]); + y1 = _cairo_fixed_from_double(scaledPt[1]); - p1.x = _cairo_fixed_from_double(scaledPt[0]); - p1.y = _cairo_fixed_from_double(scaledPt[1]); - scaledPt[0] = pt3->x; - scaledPt[1] = pt3->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); + scaledPt[1] = pt3->y; - p2.x = _cairo_fixed_from_double(scaledPt[0]); - p2.y = _cairo_fixed_from_double(scaledPt[1]); - + cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - _cairo_path_curve_to(info->path, &p0, &p1, &p2); - - - return noErr; -} + x2 = _cairo_fixed_from_double(scaledPt[0]); + y2 = _cairo_fixed_from_double(scaledPt[1]); -static OSStatus MyCubicClosePathProc(void * callBackDataPtr) -{ - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - - - _cairo_path_close_path(info->path); - - - return noErr; + _cairo_path_fixed_curve_to(info->path, x0, y0, x1, y1, x2, y2); + + + return noErr; } -static cairo_status_t -_cairo_atsui_font_glyph_path( void *abstract_font, - cairo_font_scale_t *sc, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +static OSStatus MyCubicClosePathProc(void *callBackDataPtr) { - int i; - cairo_atsui_font_t *font = abstract_font; - OSStatus err; - cairo_ATSUI_glyph_path_callback_info_t info; - ATSUStyle style; - - - static ATSCubicMoveToUPP moveProc = NULL; - static ATSCubicLineToUPP lineProc = NULL; - static ATSCubicCurveToUPP curveProc = NULL; - static ATSCubicClosePathUPP closePathProc = NULL; - - - if (moveProc == NULL) - { - moveProc = NewATSCubicMoveToUPP(MyATSCubicMoveToCallback); - lineProc = NewATSCubicLineToUPP(MyATSCubicLineToCallback); - curveProc = NewATSCubicCurveToUPP(MyATSCubicCurveToCallback); - closePathProc = NewATSCubicClosePathUPP(MyCubicClosePathProc); - } - - - info.path = path; - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - for (i = 0; i < num_glyphs; i++) - { - GlyphID theGlyph = glyphs[i].index; - - - cairo_matrix_set_affine( &info.scale, - 1.0, 0.0, - 0.0, 1.0, - glyphs[i].x, glyphs[i].y); - - - err = ATSUGlyphGetCubicPaths( style, - theGlyph, - moveProc, - lineProc, - curveProc, - closePathProc, - (void *)&info, - &err); - } - - - err = ATSUDisposeStyle(style); - - - return CAIRO_STATUS_SUCCESS; -} + cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; -#pragma mark - + _cairo_path_fixed_close_path(info->path); -static cairo_status_t -_cairo_atsui_font_create_glyph(cairo_image_glyph_cache_entry_t *val) -{ - // TODO - printf("_cairo_atsui_font_create_glyph is unimplemented\n"); - - // I'm not sure if we need this, given that the ATSUI backend does no caching(?) - - - return CAIRO_STATUS_SUCCESS; + return noErr; } -cairo_font_t * -cairo_atsui_font_create(ATSUStyle style) -{ - cairo_font_scale_t scale; - cairo_font_t *scaled; - cairo_atsui_font_t *f = NULL; - - - scaled = malloc(sizeof(cairo_font_t)); - if (scaled == NULL) - return NULL; - - - f = malloc(sizeof(cairo_atsui_font_t)); - if (f) - { - if (_cairo_unscaled_font_init(&f->base, &cairo_atsui_font_backend) == CAIRO_STATUS_SUCCESS) - { - f->style = style; - - _cairo_font_init(scaled, &scale, &f->base); - - return scaled; - } - } - - - free(scaled); - - - return NULL; -} +static cairo_status_t +_cairo_atsui_font_glyph_path(void *abstract_font, + cairo_glyph_t *glyphs, int num_glyphs, + cairo_path_fixed_t *path) +{ + int i; + cairo_atsui_font_t *font = abstract_font; + OSStatus err; + cairo_ATSUI_glyph_path_callback_info_t info; + static ATSCubicMoveToUPP moveProc = NULL; + static ATSCubicLineToUPP lineProc = NULL; + static ATSCubicCurveToUPP curveProc = NULL; + static ATSCubicClosePathUPP closePathProc = NULL; + if (moveProc == NULL) { + moveProc = NewATSCubicMoveToUPP(MyATSCubicMoveToCallback); + lineProc = NewATSCubicLineToUPP(MyATSCubicLineToCallback); + curveProc = NewATSCubicCurveToUPP(MyATSCubicCurveToCallback); + closePathProc = NewATSCubicClosePathUPP(MyCubicClosePathProc); + } -#pragma mark Backend + info.path = path; + for (i = 0; i < num_glyphs; i++) { + GlyphID theGlyph = glyphs[i].index; -const cairo_font_backend_t cairo_atsui_font_backend = { - _cairo_atsui_font_create, - _cairo_atsui_font_destroy, - _cairo_atsui_font_font_extents, - _cairo_atsui_font_text_to_glyphs, - _cairo_atsui_font_glyph_extents, - _cairo_atsui_font_glyph_bbox, - _cairo_atsui_font_show_glyphs, - _cairo_atsui_font_glyph_path, - _cairo_atsui_font_create_glyph + cairo_matrix_init(&info.scale, + 1.0, 0.0, + 0.0, 1.0, glyphs[i].x, glyphs[i].y); + + + err = ATSUGlyphGetCubicPaths(font->style, + theGlyph, + moveProc, + lineProc, + curveProc, + closePathProc, (void *) &info, &err); + } + + return CAIRO_STATUS_SUCCESS; +} + + +const cairo_scaled_font_backend_t cairo_atsui_scaled_font_backend = { + _cairo_atsui_font_create, + _cairo_atsui_font_destroy_font, + _cairo_atsui_font_font_extents, + _cairo_atsui_font_text_to_glyphs, + _cairo_atsui_font_glyph_extents, + _cairo_atsui_font_glyph_bbox, + _cairo_atsui_font_show_glyphs, + _cairo_atsui_font_glyph_path, + _cairo_atsui_font_get_glyph_cache_key, }; + diff --git a/src/cairo-atsui.h b/src/cairo-atsui.h index a5b7308f8..72e2d6d15 100644 --- a/src/cairo-atsui.h +++ b/src/cairo-atsui.h @@ -38,7 +38,7 @@ #include <cairo.h> -#ifdef CAIRO_HAS_ATSUI_FONT +#if CAIRO_HAS_ATSUI_FONT /* ATSUI platform-specific font interface */ @@ -46,10 +46,10 @@ CAIRO_BEGIN_DECLS -cairo_font_t * -cairo_atsui_font_create(ATSUStyle style); - CAIRO_END_DECLS +#else /* CAIRO_HAS_ATSUI_FONT */ +# error Cairo was not compiled with support for the atsui font backend #endif /* CAIRO_HAS_ATSUI_FONT */ + #endif /* CAIRO_ATSUI_H */ diff --git a/src/cairo-cache.c b/src/cairo-cache.c index d1ad5a4e2..e95894960 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -113,7 +113,9 @@ static const cairo_cache_arrangement_t cache_arrangements [] = { #define LIVE_ENTRY_P(cache, i) \ (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) -#ifdef CAIRO_DO_SANITY_CHECKING +#ifdef NDEBUG +#define _cache_sane_state(c) +#else static void _cache_sane_state (cairo_cache_t *cache) { @@ -125,8 +127,6 @@ _cache_sane_state (cairo_cache_t *cache) /* assert (cache->used_memory <= cache->max_memory); */ assert (cache->live_entries <= cache->arrangement->size); } -#else -#define _cache_sane_state(c) #endif static void @@ -351,8 +351,9 @@ _cairo_cache_init (cairo_cache_t *cache, #endif cache->backend = backend; - cache->entries = calloc (sizeof(cairo_cache_entry_base_t *), - cache->arrangement->size); + cache->entries = calloc (cache->arrangement->size, + sizeof(cairo_cache_entry_base_t *)); + if (cache->entries == NULL) return CAIRO_STATUS_NO_MEMORY; } diff --git a/src/cairo-color.c b/src/cairo-color.c index f203d96cc..beb4a3474 100644 --- a/src/cairo-color.c +++ b/src/cairo-color.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -36,62 +37,127 @@ #include "cairoint.h" -static cairo_color_t const CAIRO_COLOR_WHITE = { - 1.0, 1.0, 1.0, 1.0, +static cairo_color_t const cairo_color_white = { + 1.0, 1.0, 1.0, 1.0, 0xffff, 0xffff, 0xffff, 0xffff }; -static void -_cairo_color_compute_shorts (cairo_color_t *color); +static cairo_color_t const cairo_color_black = { + 0.0, 0.0, 0.0, 1.0, + 0x0, 0x0, 0x0, 0xffff +}; + +static cairo_color_t const cairo_color_transparent = { + 0.0, 0.0, 0.0, 0.0, + 0x0, 0x0, 0x0, 0x0 +}; + +static cairo_color_t const cairo_color_magenta = { + 1.0, 0.0, 1.0, 1.0, + 0xffff, 0x0, 0xffff, 0xffff +}; + +const cairo_color_t * +_cairo_stock_color (cairo_stock_t stock) +{ + switch (stock) { + case CAIRO_STOCK_WHITE: + return &cairo_color_white; + case CAIRO_STOCK_BLACK: + return &cairo_color_black; + case CAIRO_STOCK_TRANSPARENT: + return &cairo_color_transparent; + } + + ASSERT_NOT_REACHED; + + /* If the user can get here somehow, give a color that indicates a + * problem. */ + return &cairo_color_magenta; +} void _cairo_color_init (cairo_color_t *color) { - *color = CAIRO_COLOR_WHITE; + *color = cairo_color_white; } void -_cairo_color_fini (cairo_color_t *color) +_cairo_color_init_rgb (cairo_color_t *color, + double red, double green, double blue) { - /* Nothing to do here */ + _cairo_color_init_rgba (color, red, green, blue, 1.0); +} + + +/* XXX: The calculation of: + + channel * 0xffff + + isn't really what we want since: + + (1.0 - epsilon) * 0xffff = 0xfffe + + In other words, given an input range of [0.0, 1.0], we have an + infinitely small range tha maps to the output value 0xffff, + (while having large, uniformly sized input ranges for all + other output values). This is undesirable, particularly when + we want to do optimizations for "opaque" colors specfied as + floating-point. +*/ +static void +_cairo_color_compute_shorts (cairo_color_t *color) +{ + color->red_short = color->red * color->alpha * 0xffff; + color->green_short = color->green * color->alpha * 0xffff; + color->blue_short = color->blue * color->alpha * 0xffff; + color->alpha_short = color->alpha * 0xffff; } void -_cairo_color_set_rgb (cairo_color_t *color, double red, double green, double blue) +_cairo_color_init_rgba (cairo_color_t *color, + double red, double green, double blue, + double alpha) { color->red = red; color->green = green; color->blue = blue; + color->alpha = alpha; _cairo_color_compute_shorts (color); } void -_cairo_color_get_rgb (const cairo_color_t *color, - double *red, double *green, double *blue) +_cairo_color_multiply_alpha (cairo_color_t *color, + double alpha) { - if (red) - *red = color->red; - if (green) - *green = color->green; - if (blue) - *blue = color->blue; + color->alpha *= alpha; + + _cairo_color_compute_shorts (color); } void -_cairo_color_set_alpha (cairo_color_t *color, double alpha) +_cairo_color_get_rgba (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha) { - color->alpha = alpha; - - _cairo_color_compute_shorts (color); + *red = color->red; + *green = color->green; + *blue = color->blue; + *alpha = color->alpha; } -static void -_cairo_color_compute_shorts (cairo_color_t *color) +void +_cairo_color_get_rgba_premultiplied (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha) { - color->red_short = (color->red * color->alpha) * 0xffff; - color->green_short = (color->green * color->alpha) * 0xffff; - color->blue_short = (color->blue * color->alpha) * 0xffff; - color->alpha_short = color->alpha * 0xffff; + *red = color->red * color->alpha; + *green = color->green * color->alpha; + *blue = color->blue * color->alpha; + *alpha = color->alpha; } - diff --git a/src/cairo-features.h.in b/src/cairo-features.h.in index a13250d97..8065be352 100644 --- a/src/cairo-features.h.in +++ b/src/cairo-features.h.in @@ -37,28 +37,26 @@ #ifndef CAIRO_FEATURES_H #define CAIRO_FEATURES_H -#define @PS_SURFACE_FEATURE@ +@PS_SURFACE_FEATURE@ -#define @PDF_SURFACE_FEATURE@ +@PDF_SURFACE_FEATURE@ -#define @PNG_SURFACE_FEATURE@ +@XLIB_SURFACE_FEATURE@ -#define @XLIB_SURFACE_FEATURE@ +@QUARTZ_SURFACE_FEATURE@ -#define @QUARTZ_SURFACE_FEATURE@ +@XCB_SURFACE_FEATURE@ -#define @XCB_SURFACE_FEATURE@ +@WIN32_SURFACE_FEATURE@ -#define @WIN32_SURFACE_FEATURE@ +@GLITZ_SURFACE_FEATURE@ -#define @GLITZ_SURFACE_FEATURE@ +@FT_FONT_FEATURE@ -#define @FT_FONT_FEATURE@ +@WIN32_FONT_FEATURE@ -#define @WIN32_FONT_FEATURE@ +@ATSUI_FONT_FEATURE@ -#define @ATSUI_FONT_FEATURE@ - -#define @SANITY_CHECKING_FEATURE@ +@PNG_FUNCTIONS_FEATURE@ #endif diff --git a/src/cairo-font.c b/src/cairo-font.c index 529c1c7c3..3bd1e0318 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -39,185 +39,906 @@ #include "cairoint.h" -/* Now the internal "unscaled + scale" font API */ +/* cairo_font_face_t */ -cairo_private cairo_status_t -_cairo_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *sc, - cairo_font_t **font) +void +_cairo_font_face_init (cairo_font_face_t *font_face, + const cairo_font_face_backend_t *backend) { - const cairo_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; + font_face->refcount = 1; + font_face->backend = backend; - return backend->create (family, slant, weight, sc, font); + _cairo_user_data_array_init (&font_face->user_data); } +/** + * cairo_font_face_reference: + * @font_face: a #cairo_font_face_t + * + * Increases the reference count on @font_face by one. This prevents + * @font_face from being destroyed until a matching call to cairo_font_face_destroy() + * is made. + **/ void -_cairo_font_init (cairo_font_t *font, - cairo_font_scale_t *scale, - const cairo_font_backend_t *backend) +cairo_font_face_reference (cairo_font_face_t *font_face) { - font->scale = *scale; - font->refcount = 1; - font->backend = backend; + font_face->refcount++; } +/** + * cairo_font_face_destroy: + * @font_face: a #cairo_font_face_t + * + * Decreases the reference count on @font_face by one. If the result + * is zero, then @font_face and all associated resources are freed. + * See cairo_font_face_reference(). + **/ void -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const cairo_font_backend_t *backend) +cairo_font_face_destroy (cairo_font_face_t *font_face) { - font->refcount = 1; - font->backend = backend; + if (--(font_face->refcount) > 0) + return; + + font_face->backend->destroy (font_face); + + /* We allow resurrection to deal with some memory management for the + * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t + * need to effectively mutually reference each other + */ + if (font_face->refcount > 0) + return; + + _cairo_user_data_array_destroy (&font_face->user_data); + + free (font_face); } +/** + * cairo_font_face_get_user_data: + * @font_face: a #cairo_font_face_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @font_face using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +cairo_font_face_get_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&font_face->user_data, + key); +} + +/** + * cairo_font_face_set_user_data: + * @font_face: a #cairo_font_face_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the font face + * @destroy: a #cairo_destroy_func_t which will be called when the + * font face is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @font_face. To remove user data from a font face, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ cairo_status_t -_cairo_font_text_to_glyphs (cairo_font_t *font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) +cairo_font_face_set_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + return _cairo_user_data_array_set_data (&font_face->user_data, + key, user_data, destroy); +} + +/* cairo_simple_font_face_t - simple family/slant/weight font faces used for + * the built-in font API + */ + +typedef struct _cairo_simple_font_face cairo_simple_font_face_t; + +struct _cairo_simple_font_face { + cairo_font_face_t base; + char *family; + cairo_font_slant_t slant; + cairo_font_weight_t weight; +}; + +static const cairo_font_face_backend_t _cairo_simple_font_face_backend; + +/* We maintain a global cache from family/weight/slant => cairo_font_face_t + * for cairo_simple_font_t. The primary purpose of this cache is to provide + * unique cairo_font_face_t values so that our cache from + * cairo_font_face_t => cairo_scaled_font_t works. For this reason, we don't need + * this cache to keep font faces alive; we just add them to the cache and + * remove them again when freed. + */ + +typedef struct { + cairo_cache_entry_base_t base; + const char *family; + cairo_font_slant_t slant; + cairo_font_weight_t weight; +} cairo_simple_cache_key_t; + +typedef struct { + cairo_simple_cache_key_t key; + cairo_simple_font_face_t *font_face; +} cairo_simple_cache_entry_t; + +static const cairo_cache_backend_t _cairo_simple_font_cache_backend; + +static void +_lock_global_simple_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static void +_unlock_global_simple_cache (void) { - return font->backend->text_to_glyphs (font, utf8, glyphs, num_glyphs); + /* FIXME: Perform locking here. */ +} + +static cairo_cache_t * +_get_global_simple_cache (void) +{ + static cairo_cache_t *global_simple_cache = NULL; + + if (global_simple_cache == NULL) + { + global_simple_cache = malloc (sizeof (cairo_cache_t)); + if (!global_simple_cache) + goto FAIL; + + if (_cairo_cache_init (global_simple_cache, + &_cairo_simple_font_cache_backend, + 0)) /* No memory limit */ + goto FAIL; + } + return global_simple_cache; + + FAIL: + if (global_simple_cache) + free (global_simple_cache); + global_simple_cache = NULL; + return NULL; +} + +static unsigned long +_cairo_simple_font_cache_hash (void *cache, void *key) +{ + cairo_simple_cache_key_t *k = (cairo_simple_cache_key_t *) key; + unsigned long hash; + + /* 1607 and 1451 are just a couple random primes. */ + hash = _cairo_hash_string (k->family); + hash += ((unsigned long) k->slant) * 1607; + hash += ((unsigned long) k->weight) * 1451; + + return hash; +} + +static int +_cairo_simple_font_cache_keys_equal (void *cache, + void *k1, + void *k2) +{ + cairo_simple_cache_key_t *a; + cairo_simple_cache_key_t *b; + a = (cairo_simple_cache_key_t *) k1; + b = (cairo_simple_cache_key_t *) k2; + + return strcmp (a->family, b->family) == 0 && + a->slant == b->slant && + a->weight == b->weight; +} + +static cairo_simple_font_face_t * +_cairo_simple_font_face_create_from_cache_key (cairo_simple_cache_key_t *key) +{ + cairo_simple_font_face_t *simple_face; + + simple_face = malloc (sizeof (cairo_simple_font_face_t)); + if (!simple_face) + return NULL; + + simple_face->family = strdup (key->family); + if (!simple_face->family) { + free (simple_face); + return NULL; + } + + simple_face->slant = key->slant; + simple_face->weight = key->weight; + + _cairo_font_face_init (&simple_face->base, &_cairo_simple_font_face_backend); + + return simple_face; +} + +static cairo_status_t +_cairo_simple_font_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + cairo_simple_cache_key_t *k = (cairo_simple_cache_key_t *) key; + cairo_simple_cache_entry_t *entry; + + entry = malloc (sizeof (cairo_simple_cache_entry_t)); + if (entry == NULL) + return CAIRO_STATUS_NO_MEMORY; + + entry->font_face = _cairo_simple_font_face_create_from_cache_key (k); + if (!entry->font_face) { + free (entry); + return CAIRO_STATUS_NO_MEMORY; + } + + entry->key.base.memory = 0; + entry->key.family = entry->font_face->family; + entry->key.slant = entry->font_face->slant; + entry->key.weight = entry->font_face->weight; + + *return_entry = entry; + + return CAIRO_STATUS_SUCCESS; +} + +/* Entries are never spontaneously destroyed; but only when + * we remove them from the cache specifically. We free entry->font_face + * in the code that removes the entry from the cache + */ +static void +_cairo_simple_font_cache_destroy_entry (void *cache, + void *entry) +{ + cairo_simple_cache_entry_t *e = (cairo_simple_cache_entry_t *) entry; + + free (e); +} + +static void +_cairo_simple_font_cache_destroy_cache (void *cache) +{ + free (cache); +} + +static const cairo_cache_backend_t _cairo_simple_font_cache_backend = { + _cairo_simple_font_cache_hash, + _cairo_simple_font_cache_keys_equal, + _cairo_simple_font_cache_create_entry, + _cairo_simple_font_cache_destroy_entry, + _cairo_simple_font_cache_destroy_cache +}; + +static void +_cairo_simple_font_face_destroy (void *abstract_face) +{ + cairo_simple_font_face_t *simple_face = abstract_face; + cairo_cache_t *cache; + cairo_simple_cache_key_t key; + + _lock_global_simple_cache (); + cache = _get_global_simple_cache (); + assert (cache); + + key.family = simple_face->family; + key.slant = simple_face->slant; + key.weight = simple_face->weight; + + _cairo_cache_remove (cache, &key); + + _unlock_global_simple_cache (); + + free (simple_face->family); +} + +static cairo_status_t +_cairo_simple_font_face_create_font (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **scaled_font) +{ + const cairo_scaled_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; + cairo_simple_font_face_t *simple_face = abstract_face; + + return backend->create (simple_face->family, simple_face->slant, simple_face->weight, + font_matrix, ctm, scaled_font); +} + +static const cairo_font_face_backend_t _cairo_simple_font_face_backend = { + _cairo_simple_font_face_destroy, + _cairo_simple_font_face_create_font, +}; + +/** + * _cairo_simple_font_face_create: + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Creates a font face from a triplet of family, slant, and weight. + * These font faces are used in implementation of the the #cairo_t "toy" + * font API. + * + * Return value: a newly created #cairo_font_face_t, destroy with + * cairo_font_face_destroy() + **/ +cairo_private cairo_font_face_t * +_cairo_simple_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + cairo_simple_cache_entry_t *entry; + cairo_simple_cache_key_t key; + cairo_cache_t *cache; + cairo_status_t status; + cairo_bool_t created_entry; + + key.family = family; + key.slant = slant; + key.weight = weight; + + _lock_global_simple_cache (); + cache = _get_global_simple_cache (); + if (cache == NULL) { + _unlock_global_simple_cache (); + return NULL; + } + status = _cairo_cache_lookup (cache, &key, (void **) &entry, &created_entry); + if (CAIRO_OK (status) && !created_entry) + cairo_font_face_reference (&entry->font_face->base); + + _unlock_global_simple_cache (); + if (status) + return NULL; + + return &entry->font_face->base; +} + +/* cairo_scaled_font_t */ + +/* Here we keep a cache from cairo_font_face_t/matrix/ctm => cairo_scaled_font_t. + * + * The implementation is messy because we want + * + * - All otherwise referenced cairo_scaled_font_t's to be in the cache + * - Some number of not otherwise referenced cairo_scaled_font_t's + * + * For this reason, we actually use *two* caches ... a finite size + * cache that references the cairo_scaled_font_t as a first level (the outer + * cache), then an infinite size cache as the second level (the inner + * cache). A single cache could be used at the cost of complicating + * cairo-cache.c + */ + +/* This defines the size of the outer cache ... that is, the number + * of scaled fonts we keep around even when not otherwise referenced + */ +#define MAX_CACHED_FONTS 24 + +typedef struct { + cairo_cache_entry_base_t base; + cairo_font_face_t *font_face; + const cairo_matrix_t *font_matrix; + const cairo_matrix_t *ctm; +} cairo_font_cache_key_t; + +typedef struct { + cairo_font_cache_key_t key; + cairo_scaled_font_t *scaled_font; +} cairo_font_cache_entry_t; + +static const cairo_cache_backend_t _cairo_outer_font_cache_backend; +static const cairo_cache_backend_t _cairo_inner_font_cache_backend; + +static void +_lock_global_font_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static void +_unlock_global_font_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static cairo_cache_t * +_get_outer_font_cache (void) +{ + static cairo_cache_t *outer_font_cache = NULL; + + if (outer_font_cache == NULL) + { + outer_font_cache = malloc (sizeof (cairo_cache_t)); + if (!outer_font_cache) + goto FAIL; + + if (_cairo_cache_init (outer_font_cache, + &_cairo_outer_font_cache_backend, + MAX_CACHED_FONTS)) + goto FAIL; + } + return outer_font_cache; + + FAIL: + if (outer_font_cache) + free (outer_font_cache); + outer_font_cache = NULL; + return NULL; +} + +static cairo_cache_t * +_get_inner_font_cache (void) +{ + static cairo_cache_t *inner_font_cache = NULL; + + if (inner_font_cache == NULL) + { + inner_font_cache = malloc (sizeof (cairo_cache_t)); + if (!inner_font_cache) + goto FAIL; + + if (_cairo_cache_init (inner_font_cache, + &_cairo_inner_font_cache_backend, + MAX_CACHED_FONTS)) + goto FAIL; + } + return inner_font_cache; + + FAIL: + if (inner_font_cache) + free (inner_font_cache); + inner_font_cache = NULL; + return NULL; +} + + +/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) + * + * Not necessarily better than a lot of other hashes, but should be OK, and + * well tested with binary data. + */ + +#define FNV_32_PRIME ((uint32_t)0x01000193) +#define FNV1_32_INIT ((uint32_t)0x811c9dc5) + +static uint32_t +_hash_bytes_fnv (unsigned char *buffer, + int len, + uint32_t hval) +{ + while (len--) { + hval *= FNV_32_PRIME; + hval ^= *buffer++; + } + + return hval; +} +static unsigned long +_cairo_font_cache_hash (void *cache, void *key) +{ + cairo_font_cache_key_t *k = (cairo_font_cache_key_t *) key; + uint32_t hash = FNV1_32_INIT; + + /* We do a bytewise hash on the font matrices */ + hash = _hash_bytes_fnv ((unsigned char *)(&k->font_matrix->xx), + sizeof(double) * 4, + hash); + hash = _hash_bytes_fnv ((unsigned char *)(&k->ctm->xx), + sizeof(double) * 4, + hash); + + return hash ^ (unsigned long)k->font_face; +} + +static int +_cairo_font_cache_keys_equal (void *cache, + void *k1, + void *k2) +{ + cairo_font_cache_key_t *a; + cairo_font_cache_key_t *b; + a = (cairo_font_cache_key_t *) k1; + b = (cairo_font_cache_key_t *) k2; + + return (a->font_face == b->font_face && + memcmp ((unsigned char *)(&a->font_matrix->xx), + (unsigned char *)(&b->font_matrix->xx), + sizeof(double) * 4) == 0 && + memcmp ((unsigned char *)(&a->ctm->xx), + (unsigned char *)(&b->ctm->xx), + sizeof(double) * 4) == 0); +} + +/* The cache lookup failed in the outer cache, so we pull + * the font from the inner cache (if that in turns fails, + * it will create the font + */ +static cairo_status_t +_cairo_outer_font_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + cairo_font_cache_entry_t *entry; + cairo_font_cache_entry_t *inner_entry; + cairo_bool_t created_entry; + cairo_status_t status; + + entry = malloc (sizeof (cairo_font_cache_entry_t)); + if (entry == NULL) + return CAIRO_STATUS_NO_MEMORY; + + cache = _get_inner_font_cache (); + if (cache == NULL) { + _unlock_global_font_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + status = _cairo_cache_lookup (cache, key, (void **) &inner_entry, &created_entry); + if (!CAIRO_OK (status)) { + free (entry); + return status; + } + + entry->scaled_font = inner_entry->scaled_font; + if (!created_entry) + cairo_scaled_font_reference (entry->scaled_font); + + entry->key.base.memory = 1; + entry->key.font_face = entry->scaled_font->font_face; + entry->key.font_matrix = &entry->scaled_font->font_matrix; + entry->key.ctm = &entry->scaled_font->ctm; + + *return_entry = entry; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_outer_font_cache_destroy_entry (void *cache, + void *entry) +{ + cairo_font_cache_entry_t *e = (cairo_font_cache_entry_t *) entry; + + cairo_scaled_font_destroy (e->scaled_font); + + free (e); +} + +/* Called when the lookup fails in the inner cache as well; there + * is no existing font, so we have to create one. + */ +static cairo_status_t +_cairo_inner_font_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + cairo_font_cache_key_t *k = (cairo_font_cache_key_t *) key; + cairo_font_cache_entry_t *entry; + cairo_status_t status; + + entry = malloc (sizeof (cairo_font_cache_entry_t)); + if (entry == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = k->font_face->backend->create_font (k->font_face, + k->font_matrix, + k->ctm, + &entry->scaled_font); + if (!CAIRO_OK (status)) { + free (entry); + return status; + } + + entry->scaled_font->font_face = k->font_face; + cairo_font_face_reference (k->font_face); + + entry->key.base.memory = 0; + entry->key.font_face = k->font_face; + entry->key.font_matrix = &entry->scaled_font->font_matrix; + entry->key.ctm = &entry->scaled_font->ctm; + + *return_entry = entry; + + return CAIRO_STATUS_SUCCESS; +} + +/* Entries in the inner font cache are never spontaneously destroyed; + * but only when we remove them from the cache specifically. We free + * entry->scaled_font in the code that removes the entry from the cache + */ +static void +_cairo_inner_font_cache_destroy_entry (void *cache, + void *entry) +{ + free (entry); +} + +static void +_cairo_font_cache_destroy_cache (void *cache) +{ + free (cache); +} + +static const cairo_cache_backend_t _cairo_outer_font_cache_backend = { + _cairo_font_cache_hash, + _cairo_font_cache_keys_equal, + _cairo_outer_font_cache_create_entry, + _cairo_outer_font_cache_destroy_entry, + _cairo_font_cache_destroy_cache +}; + +static const cairo_cache_backend_t _cairo_inner_font_cache_backend = { + _cairo_font_cache_hash, + _cairo_font_cache_keys_equal, + _cairo_inner_font_cache_create_entry, + _cairo_inner_font_cache_destroy_entry, + _cairo_font_cache_destroy_cache +}; + +/** + * cairo_scaled_font_create: + * @font_face: a #cairo_font_face_t + * @font_matrix: font space to user space transformation matrix for the + * font. In the simplest case of a N point font, this matrix is + * just a scale by N, but it can also be used to shear the font + * or stretch it unequally along the two axes. See + * cairo_set_font_matrix(). + * @ctm: user to device transformation matrix with which the font will + * be used. + * + * Creates a #cairo_scaled_font_t object from a font face and matrices that + * describe the size of the font and the environment in which it will + * be used. + * + * Return value: a newly created #cairo_scaled_font_t. Destroy with + * cairo_scaled_font_destroy() + **/ +cairo_scaled_font_t * +cairo_scaled_font_create (cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm) +{ + cairo_font_cache_entry_t *entry; + cairo_font_cache_key_t key; + cairo_cache_t *cache; + cairo_status_t status; + + key.font_face = font_face; + key.font_matrix = font_matrix; + key.ctm = ctm; + + _lock_global_font_cache (); + cache = _get_outer_font_cache (); + if (cache == NULL) { + _unlock_global_font_cache (); + return NULL; + } + + status = _cairo_cache_lookup (cache, &key, (void **) &entry, NULL); + if (CAIRO_OK (status)) + cairo_scaled_font_reference (entry->scaled_font); + + _unlock_global_font_cache (); + if (!CAIRO_OK (status)) + return NULL; + + return entry->scaled_font; +} + +void +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_scaled_font_backend_t *backend) +{ + scaled_font->font_matrix = *font_matrix; + scaled_font->ctm = *ctm; + cairo_matrix_multiply (&scaled_font->scale, &scaled_font->font_matrix, &scaled_font->ctm); + + scaled_font->refcount = 1; + scaled_font->backend = backend; } cairo_status_t -_cairo_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - return font->backend->glyph_extents(font, glyphs, num_glyphs, extents); + return scaled_font->backend->text_to_glyphs (scaled_font, utf8, glyphs, num_glyphs); +} + +cairo_status_t +_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + return scaled_font->backend->glyph_extents (scaled_font, glyphs, num_glyphs, extents); } cairo_status_t -_cairo_font_glyph_bbox (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_scaled_font_glyph_bbox (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { - return font->backend->glyph_bbox (font, glyphs, num_glyphs, bbox); + return scaled_font->backend->glyph_bbox (scaled_font, glyphs, num_glyphs, bbox); } cairo_status_t -_cairo_font_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs) { cairo_status_t status; - if (surface->backend->show_glyphs != NULL) { - status = surface->backend->show_glyphs (font, operator, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs); - if (status == CAIRO_STATUS_SUCCESS) - return status; - } + + status = _cairo_surface_show_glyphs (scaled_font, operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; /* Surface display routine either does not exist or failed. */ - return font->backend->show_glyphs (font, operator, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs); + return scaled_font->backend->show_glyphs (scaled_font, operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs); } cairo_status_t -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) { - return font->backend->glyph_path (font, glyphs, num_glyphs, path); + return scaled_font->backend->glyph_path (scaled_font, glyphs, num_glyphs, path); } void -_cairo_font_get_glyph_cache_key (cairo_font_t *font, - cairo_glyph_cache_key_t *key) +_cairo_scaled_font_get_glyph_cache_key (cairo_scaled_font_t *scaled_font, + cairo_glyph_cache_key_t *key) { - font->backend->get_glyph_cache_key (font, key); + scaled_font->backend->get_glyph_cache_key (scaled_font, key); } cairo_status_t -_cairo_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents) +_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents) { - return font->backend->font_extents (font, extents); + return scaled_font->backend->font_extents (scaled_font, extents); } void -_cairo_unscaled_font_reference (cairo_unscaled_font_t *font) +_cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, + const cairo_unscaled_font_backend_t *backend) { - font->refcount++; + unscaled_font->refcount = 1; + unscaled_font->backend = backend; } void -_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font) +_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font) +{ + unscaled_font->refcount++; +} + +void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) { - if (--(font->refcount) > 0) + if (--(unscaled_font->refcount) > 0) return; - font->backend->destroy_unscaled_font (font); + unscaled_font->backend->destroy (unscaled_font); + + free (unscaled_font); } /* Public font API follows. */ +/** + * cairo_scaled_font_reference: + * @scaled_font: a #cairo_scaled_font_t + * + * Increases the reference count on @scaled_font by one. This prevents + * @scaled_font from being destroyed until a matching call to + * cairo_scaled_font_destroy() is made. + **/ void -cairo_font_reference (cairo_font_t *font) +cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) { - font->refcount++; + scaled_font->refcount++; } +/** + * cairo_scaled_font_destroy: + * @scaled_font: a #cairo_scaled_font_t + * + * Decreases the reference count on @font by one. If the result + * is zero, then @font and all associated resources are freed. + * See cairo_scaled_font_reference(). + **/ void -cairo_font_destroy (cairo_font_t *font) +cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) { - if (--(font->refcount) > 0) + cairo_font_cache_key_t key; + cairo_cache_t *cache; + + if (--(scaled_font->refcount) > 0) return; - font->backend->destroy_font (font); + if (scaled_font->font_face) { + _lock_global_font_cache (); + cache = _get_inner_font_cache (); + assert (cache); + + key.font_face = scaled_font->font_face; + key.font_matrix = &scaled_font->font_matrix; + key.ctm = &scaled_font->ctm; + + _cairo_cache_remove (cache, &key); + _unlock_global_font_cache (); + + cairo_font_face_destroy (scaled_font->font_face); + } + + scaled_font->backend->destroy (scaled_font); + + free (scaled_font); } /** - * cairo_font_extents: - * @font: a #cairo_font_t - * @font_matrix: the font transformation for which this font was - * created. (See cairo_transform_font()). This is needed - * properly convert the metrics from the font into user space. + * cairo_scaled_font_extents: + * @scaled_font: a #cairo_scaled_font_t * @extents: a #cairo_font_extents_t which to store the retrieved extents. * - * Gets the metrics for a #cairo_font_t. + * Gets the metrics for a #cairo_scaled_font_t. * * Return value: %CAIRO_STATUS_SUCCESS on success. Otherwise, an * error such as %CAIRO_STATUS_NO_MEMORY. **/ cairo_status_t -cairo_font_extents (cairo_font_t *font, - cairo_matrix_t *font_matrix, - cairo_font_extents_t *extents) +cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents) { cairo_int_status_t status; double font_scale_x, font_scale_y; - status = _cairo_font_font_extents (font, extents); + status = _cairo_scaled_font_font_extents (scaled_font, extents); if (!CAIRO_OK (status)) return status; - _cairo_matrix_compute_scale_factors (font_matrix, + _cairo_matrix_compute_scale_factors (&scaled_font->font_matrix, &font_scale_x, &font_scale_y, /* XXX */ 1); @@ -237,10 +958,7 @@ cairo_font_extents (cairo_font_t *font, /** * cairo_font_glyph_extents: - * @font: a #cairo_font_t - * @font_matrix: the font transformation for which this font was - * created. (See cairo_transform_font()). This is needed - * properly convert the metrics from the font into user space. + * @scaled_font: a #cairo_scaled_font_t * @glyphs: an array of glyph IDs with X and Y offsets. * @num_glyphs: the number of glyphs in the @glyphs array * @extents: a #cairo_text_extents_t which to store the retrieved extents. @@ -249,11 +967,10 @@ cairo_font_extents (cairo_font_t *font, * glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0. **/ void -cairo_font_glyph_extents (cairo_font_t *font, - cairo_matrix_t *font_matrix, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_glyph_t origin_glyph; @@ -283,9 +1000,9 @@ cairo_font_glyph_extents (cairo_font_t *font, origin_glyph = glyphs[i]; origin_glyph.x = 0.0; origin_glyph.y = 0.0; - status = _cairo_font_glyph_extents (font, - &origin_glyph, 1, - &origin_extents); + status = _cairo_scaled_font_glyph_extents (scaled_font, + &origin_glyph, 1, + &origin_extents); /* * Transform font space metrics into user space metrics @@ -294,7 +1011,7 @@ cairo_font_glyph_extents (cairo_font_t *font, */ x = origin_extents.x_bearing; y = origin_extents.y_bearing; - cairo_matrix_transform_point (font_matrix, + cairo_matrix_transform_point (&scaled_font->font_matrix, &x, &y); for (hm = 0.0; hm <= 1.0; hm += 1.0) @@ -302,7 +1019,7 @@ cairo_font_glyph_extents (cairo_font_t *font, { x = origin_extents.x_bearing + origin_extents.width * wm; y = origin_extents.y_bearing + origin_extents.height * hm; - cairo_matrix_transform_point (font_matrix, + cairo_matrix_transform_point (&scaled_font->font_matrix, &x, &y); x += glyphs[i].x; y += glyphs[i].y; @@ -323,7 +1040,7 @@ cairo_font_glyph_extents (cairo_font_t *font, x = origin_extents.x_advance; y = origin_extents.y_advance; - cairo_matrix_transform_point (font_matrix, + cairo_matrix_transform_point (&scaled_font->font_matrix, &x, &y); x_pos = glyphs[i].x + x; y_pos = glyphs[i].y + y; @@ -348,10 +1065,10 @@ _cairo_glyph_cache_hash (void *cache, void *key) in = (cairo_glyph_cache_key_t *) key; return ((unsigned long) in->unscaled) - ^ ((unsigned long) in->scale.matrix[0][0]) - ^ ((unsigned long) in->scale.matrix[0][1]) - ^ ((unsigned long) in->scale.matrix[1][0]) - ^ ((unsigned long) in->scale.matrix[1][1]) + ^ ((unsigned long) in->scale.xx) + ^ ((unsigned long) in->scale.yx) + ^ ((unsigned long) in->scale.xy) + ^ ((unsigned long) in->scale.yy) ^ (in->flags * 1451) /* 1451 is just an abitrary prime */ ^ in->index; } @@ -367,10 +1084,10 @@ _cairo_glyph_cache_keys_equal (void *cache, return (a->index == b->index) && (a->unscaled == b->unscaled) && (a->flags == b->flags) - && (a->scale.matrix[0][0] == b->scale.matrix[0][0]) - && (a->scale.matrix[0][1] == b->scale.matrix[0][1]) - && (a->scale.matrix[1][0] == b->scale.matrix[1][0]) - && (a->scale.matrix[1][1] == b->scale.matrix[1][1]); + && (a->scale.xx == b->scale.xx) + && (a->scale.yx == b->scale.yx) + && (a->scale.xy == b->scale.xy) + && (a->scale.yy == b->scale.yy); } @@ -388,7 +1105,8 @@ _image_glyph_cache_create_entry (void *cache, return CAIRO_STATUS_NO_MEMORY; im->key = *k; - status = im->key.unscaled->backend->create_glyph (im); + status = im->key.unscaled->backend->create_glyph (im->key.unscaled, + im); if (status != CAIRO_STATUS_SUCCESS) { free (im); diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 44e1b0e84..c02cd61e5 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -66,31 +66,42 @@ typedef struct { /* * We create an object that corresponds to a single font on the disk; * (identified by a filename/id pair) these are shared between all - * fonts using that file. For cairo_ft_font_create_for_ft_face(), we + * fonts using that file. For cairo_ft_scaled_font_create_for_ft_face(), we * just create a one-off version with a permanent face value. */ + +typedef struct _ft_font_face ft_font_face_t; + typedef struct { cairo_unscaled_font_t base; - int from_face; /* from cairo_ft_font_create_for_ft_face()? */ - FT_Face face; /* provided or cached face */ + cairo_bool_t from_face; /* from cairo_ft_scaled_font_create_for_ft_face()? */ + FT_Face face; /* provided or cached face */ /* only set if from_face is false */ - FT_Library library; char *filename; int id; /* We temporarily scale the unscaled font as neede */ int have_scale; - cairo_font_scale_t current_scale; + cairo_matrix_t current_scale; double x_scale; /* Extracted X scale factor */ double y_scale; /* Extracted Y scale factor */ int lock; /* count of how many times this font has been locked */ + + ft_font_face_t *faces; /* Linked list of faces for this font */ } ft_unscaled_font_t; -const cairo_font_backend_t cairo_ft_font_backend; +struct _ft_font_face { + cairo_font_face_t base; + ft_unscaled_font_t *unscaled; + int load_flags; + ft_font_face_t *next_face; +}; + +const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend; static ft_unscaled_font_t * _ft_unscaled_font_create_from_face (FT_Face face) @@ -102,21 +113,21 @@ _ft_unscaled_font_create_from_face (FT_Face face) unscaled->from_face = 1; unscaled->face = face; - unscaled->library = NULL; unscaled->filename = NULL; unscaled->id = 0; unscaled->have_scale = 0; unscaled->lock = 0; - _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, - &cairo_ft_font_backend); + unscaled->faces = NULL; + + _cairo_unscaled_font_init (&unscaled->base, + &cairo_ft_unscaled_font_backend); return unscaled; } static ft_unscaled_font_t * -_ft_unscaled_font_create_from_filename (FT_Library library, - const char *filename, +_ft_unscaled_font_create_from_filename (const char *filename, int id) { ft_unscaled_font_t *unscaled; @@ -135,15 +146,16 @@ _ft_unscaled_font_create_from_filename (FT_Library library, unscaled->from_face = 0; unscaled->face = NULL; - unscaled->library = library; unscaled->filename = new_filename; unscaled->id = id; unscaled->have_scale = 0; unscaled->lock = 0; - _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, - &cairo_ft_font_backend); + unscaled->faces = NULL; + + _cairo_unscaled_font_init (&unscaled->base, + &cairo_ft_unscaled_font_backend); return unscaled; } @@ -204,7 +216,6 @@ _ft_font_cache_create_entry (void *cache, void *key, void **return_entry) { - ft_cache_t *ftcache = (ft_cache_t *) cache; cairo_ft_cache_key_t *k = (cairo_ft_cache_key_t *) key; cairo_ft_cache_entry_t *entry; @@ -212,8 +223,7 @@ _ft_font_cache_create_entry (void *cache, if (entry == NULL) return CAIRO_STATUS_NO_MEMORY; - entry->unscaled = _ft_unscaled_font_create_from_filename (ftcache->lib, - k->filename, + entry->unscaled = _ft_unscaled_font_create_from_filename (k->filename, k->id); if (!entry->unscaled) { free (entry); @@ -327,13 +337,13 @@ _ft_unscaled_font_get_for_pattern (FcPattern *pattern) } status = _cairo_cache_lookup (cache, &key, (void **) &entry, &created_entry); + if (CAIRO_OK (status) && !created_entry) + _cairo_unscaled_font_reference (&entry->unscaled->base); + _unlock_global_ft_cache (); - if (status) + if (!CAIRO_OK (status)) return NULL; - if (!created_entry) - _cairo_unscaled_font_reference ((cairo_unscaled_font_t *)entry->unscaled); - return entry->unscaled; } @@ -406,9 +416,9 @@ _ft_unscaled_font_unlock_face (ft_unscaled_font_t *unscaled) static void _compute_transform (ft_font_transform_t *sf, - cairo_font_scale_t *sc) + cairo_matrix_t *scale) { - cairo_matrix_t normalized; + cairo_matrix_t normalized = *scale; double tx, ty; /* The font matrix has x and y "scale" components which we extract and @@ -418,21 +428,14 @@ _compute_transform (ft_font_transform_t *sf, * freetype's transformation. */ - cairo_matrix_set_affine (&normalized, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - _cairo_matrix_compute_scale_factors (&normalized, &sf->x_scale, &sf->y_scale, /* XXX */ 1); cairo_matrix_scale (&normalized, 1.0 / sf->x_scale, 1.0 / sf->y_scale); - cairo_matrix_get_affine (&normalized, - &sf->shape[0][0], &sf->shape[0][1], - &sf->shape[1][0], &sf->shape[1][1], - &tx, &ty); + _cairo_matrix_get_affine (&normalized, + &sf->shape[0][0], &sf->shape[0][1], + &sf->shape[1][0], &sf->shape[1][1], + &tx, &ty); } /* Temporarily scales an unscaled font to the give scale. We catch @@ -440,7 +443,7 @@ _compute_transform (ft_font_transform_t *sf, */ static void _ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, - cairo_font_scale_t *scale) + cairo_matrix_t *scale) { ft_font_transform_t sf; FT_Matrix mat; @@ -448,10 +451,10 @@ _ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, assert (unscaled->face != NULL); if (unscaled->have_scale && - scale->matrix[0][0] == unscaled->current_scale.matrix[0][0] && - scale->matrix[0][1] == unscaled->current_scale.matrix[0][1] && - scale->matrix[1][0] == unscaled->current_scale.matrix[1][0] && - scale->matrix[1][1] == unscaled->current_scale.matrix[1][1]) + scale->xx == unscaled->current_scale.xx && + scale->yx == unscaled->current_scale.yx && + scale->xy == unscaled->current_scale.xy && + scale->yy == unscaled->current_scale.yy) return; unscaled->have_scale = 1; @@ -474,14 +477,172 @@ _ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, (FT_UInt) sf.y_scale); } -/* implement the font backend interface */ +static void +_cairo_ft_unscaled_font_destroy (void *abstract_font) +{ + ft_unscaled_font_t *unscaled = abstract_font; + + if (unscaled->from_face) { + /* See comments in _ft_font_face_destroy about the "zombie" state + * for a _ft_font_face. + */ + if (unscaled->faces && !unscaled->faces->unscaled) + cairo_font_face_destroy (&unscaled->faces->base); + } else { + cairo_cache_t *cache; + cairo_ft_cache_key_t key; + + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + assert (cache); + + key.filename = unscaled->filename; + key.id = unscaled->id; + + _cairo_cache_remove (cache, &key); + + _unlock_global_ft_cache (); + + if (unscaled->filename) + free (unscaled->filename); + + if (unscaled->face) + FT_Done_Face (unscaled->face); + } +} + +static cairo_status_t +_cairo_ft_unscaled_font_create_glyph (void *abstract_font, + cairo_image_glyph_cache_entry_t *val) +{ + ft_unscaled_font_t *unscaled = abstract_font; + FT_GlyphSlot glyphslot; + unsigned int width, height, stride; + FT_Face face; + FT_Outline *outline; + FT_BBox cbox; + FT_Bitmap bitmap; + FT_Glyph_Metrics *metrics; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + face = _ft_unscaled_font_lock_face (unscaled); + if (!face) + return CAIRO_STATUS_NO_MEMORY; + + glyphslot = face->glyph; + metrics = &glyphslot->metrics; + + _ft_unscaled_font_set_scale (unscaled, &val->key.scale); + + if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinates of the bearing and advance need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + */ + + val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; + val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; + + val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; + val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; + + /* + * use untransformed advance values + * XXX uses horizontal advance only at present; + should provide FT_LOAD_VERTICAL_LAYOUT + */ + + val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; + val->extents.y_advance = 0 / unscaled->y_scale; + + outline = &glyphslot->outline; + + FT_Outline_Get_CBox (outline, &cbox); + + cbox.xMin &= -64; + cbox.yMin &= -64; + cbox.xMax = (cbox.xMax + 63) & -64; + cbox.yMax = (cbox.yMax + 63) & -64; + + width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); + height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); + stride = (width + 3) & -4; + + if (width * height == 0) + val->image = NULL; + else + { + + bitmap.pixel_mode = ft_pixel_mode_grays; + bitmap.num_grays = 256; + bitmap.width = width; + bitmap.rows = height; + bitmap.pitch = stride; + bitmap.buffer = calloc (1, stride * height); + + if (bitmap.buffer == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); + + if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { + free (bitmap.buffer); + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + val->image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (bitmap.buffer, + CAIRO_FORMAT_A8, + width, height, stride); + if (val->image == NULL) { + free (bitmap.buffer); + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + _cairo_image_surface_assume_ownership_of_data (val->image); + } + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. + */ + + val->size.width = (unsigned short) width; + val->size.height = (unsigned short) height; + val->size.x = (short) (cbox.xMin >> 6); + val->size.y = - (short) (cbox.yMax >> 6); + + FAIL: + _ft_unscaled_font_unlock_face (unscaled); + + return status; +} + +const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { + _cairo_ft_unscaled_font_destroy, + _cairo_ft_unscaled_font_create_glyph +}; + +/* cairo_ft_scaled_font_t */ typedef struct { - cairo_font_t base; - FcPattern *pattern; + cairo_scaled_font_t base; int load_flags; ft_unscaled_font_t *unscaled; -} cairo_ft_font_t; +} cairo_ft_scaled_font_t; + +const cairo_scaled_font_backend_t cairo_ft_scaled_font_backend; /* for compatibility with older freetype versions */ #ifndef FT_LOAD_TARGET_MONO @@ -547,51 +708,43 @@ _get_load_flags (FcPattern *pattern) return load_flags; } -/* Like the public cairo_ft_font_create, but takes a cairo_font_scale_t, - * rather than a cairo_font_t - */ -static cairo_font_t * -_ft_font_create (FcPattern *pattern, - cairo_font_scale_t *scale) +static cairo_scaled_font_t * +_ft_scaled_font_create (ft_unscaled_font_t *unscaled, + int load_flags, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm) { - cairo_ft_font_t *f = NULL; - ft_unscaled_font_t *unscaled = NULL; + cairo_ft_scaled_font_t *f = NULL; - unscaled = _ft_unscaled_font_get_for_pattern (pattern); - if (unscaled == NULL) - return NULL; - - f = malloc (sizeof(cairo_ft_font_t)); + f = malloc (sizeof(cairo_ft_scaled_font_t)); if (f == NULL) - goto FREE_UNSCALED; + return NULL; f->unscaled = unscaled; - f->pattern = pattern; - FcPatternReference (pattern); - f->load_flags = _get_load_flags (pattern); - - _cairo_font_init ((cairo_font_t *)f, scale, &cairo_ft_font_backend); - - return (cairo_font_t *)f; + _cairo_unscaled_font_reference (&unscaled->base); + + f->load_flags = load_flags; - FREE_UNSCALED: - _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); + _cairo_scaled_font_init (&f->base, font_matrix, ctm, &cairo_ft_scaled_font_backend); - return NULL; + return (cairo_scaled_font_t *)f; } static cairo_status_t -_cairo_ft_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *scale, - cairo_font_t **font) +_cairo_ft_scaled_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **font) { FcPattern *pattern, *resolved; - cairo_font_t *new_font; + ft_unscaled_font_t *unscaled; + cairo_scaled_font_t *new_font; FcResult result; int fcslant; int fcweight; + cairo_matrix_t scale; ft_font_transform_t sf; pattern = FcPatternCreate (); @@ -623,14 +776,15 @@ _cairo_ft_font_create (const char *family, break; } - if (!FcPatternAddString (pattern, FC_FAMILY, family)) + if (!FcPatternAddString (pattern, FC_FAMILY, (unsigned char *) family)) goto FREE_PATTERN; if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) goto FREE_PATTERN; if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) goto FREE_PATTERN; - _compute_transform (&sf, scale); + cairo_matrix_multiply (&scale, font_matrix, ctm); + _compute_transform (&sf, &scale); FcPatternAddInteger (pattern, FC_PIXEL_SIZE, sf.y_scale); @@ -641,7 +795,13 @@ _cairo_ft_font_create (const char *family, if (!resolved) goto FREE_PATTERN; - new_font = _ft_font_create (resolved, scale); + unscaled = _ft_unscaled_font_get_for_pattern (resolved); + if (!unscaled) + goto FREE_RESOLVED; + + new_font = _ft_scaled_font_create (unscaled, _get_load_flags (pattern), + font_matrix, ctm); + _cairo_unscaled_font_destroy (&unscaled->base); FcPatternDestroy (resolved); FcPatternDestroy (pattern); @@ -653,6 +813,9 @@ _cairo_ft_font_create (const char *family, return CAIRO_STATUS_NO_MEMORY; /* A guess */ } + FREE_RESOLVED: + FcPatternDestroy (resolved); + FREE_PATTERN: FcPatternDestroy (pattern); @@ -660,88 +823,50 @@ _cairo_ft_font_create (const char *family, } static void -_cairo_ft_font_destroy_font (void *abstract_font) +_cairo_ft_scaled_font_destroy (void *abstract_font) { - cairo_ft_font_t * font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; - if (font == NULL) + if (scaled_font == NULL) return; - if (font->pattern != NULL) - FcPatternDestroy (font->pattern); - - _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)font->unscaled); - - free (font); -} - -static void -_cairo_ft_font_destroy_unscaled_font (void *abstract_font) -{ - ft_unscaled_font_t *unscaled = abstract_font; - - if (!unscaled->from_face) { - cairo_cache_t *cache; - cairo_ft_cache_key_t key; - - _lock_global_ft_cache (); - cache = _get_global_ft_cache (); - assert (cache); - - key.filename = unscaled->filename; - key.id = unscaled->id; - - _cairo_cache_remove (cache, &key); - - _unlock_global_ft_cache (); - } - - if (unscaled == NULL) - return; - - if (!unscaled->from_face && unscaled->face) - FT_Done_Face (unscaled->face); - - if (unscaled->filename) - free (unscaled->filename); - - free (unscaled); + _cairo_unscaled_font_destroy (&scaled_font->unscaled->base); } static void -_cairo_ft_font_get_glyph_cache_key (void *abstract_font, - cairo_glyph_cache_key_t *key) +_cairo_ft_scaled_font_get_glyph_cache_key (void *abstract_font, + cairo_glyph_cache_key_t *key) { - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; - key->unscaled = (cairo_unscaled_font_t *)font->unscaled; - key->scale = font->base.scale; - key->flags = font->load_flags; + key->unscaled = &scaled_font->unscaled->base; + key->scale = scaled_font->base.scale; + key->flags = scaled_font->load_flags; } static cairo_status_t -_cairo_ft_font_text_to_glyphs (void *abstract_font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *nglyphs) +_cairo_ft_scaled_font_text_to_glyphs (void *abstract_font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { double x = 0., y = 0.; size_t i; uint32_t *ucs4 = NULL; - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; FT_Face face; cairo_glyph_cache_key_t key; cairo_image_glyph_cache_entry_t *val; cairo_cache_t *cache = NULL; cairo_status_t status = CAIRO_STATUS_SUCCESS; - _cairo_ft_font_get_glyph_cache_key (font, &key); + _cairo_ft_scaled_font_get_glyph_cache_key (scaled_font, &key); - status = _cairo_utf8_to_ucs4 (utf8, -1, &ucs4, nglyphs); + status = _cairo_utf8_to_ucs4 ((unsigned char*)utf8, -1, &ucs4, num_glyphs); if (!CAIRO_OK (status)) return status; - face = cairo_ft_font_lock_face ((cairo_font_t *)font); + face = cairo_ft_scaled_font_lock_face (&scaled_font->base); if (!face) { status = CAIRO_STATUS_NO_MEMORY; goto FAIL1; @@ -754,13 +879,13 @@ _cairo_ft_font_text_to_glyphs (void *abstract_font, goto FAIL2; } - *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); + *glyphs = (cairo_glyph_t *) malloc ((*num_glyphs) * (sizeof (cairo_glyph_t))); if (*glyphs == NULL) { status = CAIRO_STATUS_NO_MEMORY; goto FAIL2; } - for (i = 0; i < *nglyphs; i++) + for (i = 0; i < *num_glyphs; i++) { (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); (*glyphs)[i].x = x; @@ -781,7 +906,7 @@ _cairo_ft_font_text_to_glyphs (void *abstract_font, if (cache) _cairo_unlock_global_image_glyph_cache (); - cairo_ft_font_unlock_face ((cairo_font_t *)font); + cairo_ft_scaled_font_unlock_face (&scaled_font->base); FAIL1: free (ucs4); @@ -791,49 +916,50 @@ _cairo_ft_font_text_to_glyphs (void *abstract_font, static cairo_status_t -_cairo_ft_font_font_extents (void *abstract_font, - cairo_font_extents_t *extents) +_cairo_ft_scaled_font_font_extents (void *abstract_font, + cairo_font_extents_t *extents) { - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; FT_Face face; FT_Size_Metrics *metrics; - face = _ft_unscaled_font_lock_face (font->unscaled); + face = _ft_unscaled_font_lock_face (scaled_font->unscaled); if (!face) return CAIRO_STATUS_NO_MEMORY; metrics = &face->size->metrics; - _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); + _ft_unscaled_font_set_scale (scaled_font->unscaled, &scaled_font->base.scale); /* * Get to unscaled metrics so that the upper level can get back to * user space */ - extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / font->unscaled->y_scale; - extents->descent = DOUBLE_FROM_26_6(metrics->descender) / font->unscaled->y_scale; - extents->height = DOUBLE_FROM_26_6(metrics->height) / font->unscaled->y_scale; - extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / font->unscaled->x_scale; + extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / scaled_font->unscaled->y_scale; + extents->descent = DOUBLE_FROM_26_6(- metrics->descender) / scaled_font->unscaled->y_scale; + extents->height = DOUBLE_FROM_26_6(metrics->height) / scaled_font->unscaled->y_scale; + extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / scaled_font->unscaled->x_scale; /* FIXME: this doesn't do vertical layout atm. */ extents->max_y_advance = 0.0; - _ft_unscaled_font_unlock_face (font->unscaled); + _ft_unscaled_font_unlock_face (scaled_font->unscaled); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_ft_font_glyph_extents (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_ft_scaled_font_glyph_extents (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { int i; - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_point_double_t origin; cairo_point_double_t glyph_min, glyph_max; - cairo_point_double_t total_min, total_max; + /* Initialize just to squelch anti-helpful compiler warning. */ + cairo_point_double_t total_min = { 0, 0}, total_max = {0,0}; cairo_image_glyph_cache_entry_t *img = NULL; cairo_cache_t *cache; @@ -861,7 +987,7 @@ _cairo_ft_font_glyph_extents (void *abstract_font, return CAIRO_STATUS_NO_MEMORY; } - _cairo_ft_font_get_glyph_cache_key (font, &key); + _cairo_ft_scaled_font_get_glyph_cache_key (scaled_font, &key); for (i = 0; i < num_glyphs; i++) { @@ -874,7 +1000,7 @@ _cairo_ft_font_glyph_extents (void *abstract_font, /* XXX: Need to add code here to check the font's FcPattern for FC_VERTICAL_LAYOUT and if set get vertBearingX/Y instead. This will require that - cairo_ft_font_create_for_ft_face accept an + cairo_ft_scaled_font_create_for_ft_face accept an FcPattern. */ glyph_min.x = glyphs[i].x + img->extents.x_bearing; glyph_min.y = glyphs[i].y + img->extents.y_bearing; @@ -910,15 +1036,15 @@ _cairo_ft_font_glyph_extents (void *abstract_font, static cairo_status_t -_cairo_ft_font_glyph_bbox (void *abstract_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_ft_scaled_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { cairo_image_glyph_cache_entry_t *img; cairo_cache_t *cache; cairo_glyph_cache_key_t key; - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_fixed_t x1, y1, x2, y2; int i; @@ -930,13 +1056,13 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, cache = _cairo_get_global_image_glyph_cache(); if (cache == NULL - || font == NULL + || scaled_font == NULL || glyphs == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } - _cairo_ft_font_get_glyph_cache_key (font, &key); + _cairo_ft_scaled_font_get_glyph_cache_key (scaled_font, &key); for (i = 0; i < num_glyphs; i++) { @@ -972,23 +1098,23 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, static cairo_status_t -_cairo_ft_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_ft_scaled_font_show_glyphs (void *abstract_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) { cairo_image_glyph_cache_entry_t *img; cairo_cache_t *cache; cairo_glyph_cache_key_t key; - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_surface_pattern_t glyph_pattern; cairo_status_t status; int x, y; @@ -998,7 +1124,7 @@ _cairo_ft_font_show_glyphs (void *abstract_font, cache = _cairo_get_global_image_glyph_cache(); if (cache == NULL - || font == NULL + || scaled_font == NULL || pattern == NULL || surface == NULL || glyphs == NULL) { @@ -1006,9 +1132,9 @@ _cairo_ft_font_show_glyphs (void *abstract_font, return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = (cairo_unscaled_font_t *)font->unscaled; - key.scale = font->base.scale; - key.flags = font->load_flags; + key.unscaled = &scaled_font->unscaled->base; + key.scale = scaled_font->base.scale; + key.flags = scaled_font->load_flags; for (i = 0; i < num_glyphs; i++) { @@ -1054,14 +1180,14 @@ _cairo_ft_font_show_glyphs (void *abstract_font, static int _move_to (FT_Vector *to, void *closure) { - cairo_path_t *path = closure; - cairo_point_t point; + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; - point.x = _cairo_fixed_from_26_6 (to->x); - point.y = _cairo_fixed_from_26_6 (to->y); + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); - _cairo_path_close_path (path); - _cairo_path_move_to (path, &point); + _cairo_path_fixed_close_path (path); + _cairo_path_fixed_move_to (path, x, y); return 0; } @@ -1069,13 +1195,13 @@ _move_to (FT_Vector *to, void *closure) static int _line_to (FT_Vector *to, void *closure) { - cairo_path_t *path = closure; - cairo_point_t point; + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; - point.x = _cairo_fixed_from_26_6 (to->x); - point.y = _cairo_fixed_from_26_6 (to->y); + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); - _cairo_path_line_to (path, &point); + _cairo_path_fixed_line_to (path, x, y); return 0; } @@ -1083,27 +1209,32 @@ _line_to (FT_Vector *to, void *closure) static int _conic_to (FT_Vector *control, FT_Vector *to, void *closure) { - cairo_path_t *path = closure; + cairo_path_fixed_t *path = closure; - cairo_point_t p0, p1, p2, p3; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + cairo_fixed_t x3, y3; cairo_point_t conic; - _cairo_path_current_point (path, &p0); + _cairo_path_fixed_get_current_point (path, &x0, &y0); conic.x = _cairo_fixed_from_26_6 (control->x); conic.y = _cairo_fixed_from_26_6 (control->y); - p3.x = _cairo_fixed_from_26_6 (to->x); - p3.y = _cairo_fixed_from_26_6 (to->y); + x3 = _cairo_fixed_from_26_6 (to->x); + y3 = _cairo_fixed_from_26_6 (to->y); - p1.x = p0.x + 2.0/3.0 * (conic.x - p0.x); - p1.y = p0.y + 2.0/3.0 * (conic.y - p0.y); + x1 = x0 + 2.0/3.0 * (conic.x - x0); + y1 = y0 + 2.0/3.0 * (conic.y - y0); - p2.x = p3.x + 2.0/3.0 * (conic.x - p3.x); - p2.y = p3.y + 2.0/3.0 * (conic.y - p3.y); + x2 = x3 + 2.0/3.0 * (conic.x - x3); + y2 = y3 + 2.0/3.0 * (conic.y - y3); - _cairo_path_curve_to (path, - &p1, &p2, &p3); + _cairo_path_fixed_curve_to (path, + x1, y1, + x2, y2, + x3, y3); return 0; } @@ -1111,31 +1242,36 @@ _conic_to (FT_Vector *control, FT_Vector *to, void *closure) static int _cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closure) { - cairo_path_t *path = closure; - cairo_point_t p0, p1, p2; + cairo_path_fixed_t *path = closure; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; - p0.x = _cairo_fixed_from_26_6 (control1->x); - p0.y = _cairo_fixed_from_26_6 (control1->y); + x0 = _cairo_fixed_from_26_6 (control1->x); + y0 = _cairo_fixed_from_26_6 (control1->y); - p1.x = _cairo_fixed_from_26_6 (control2->x); - p1.y = _cairo_fixed_from_26_6 (control2->y); + x1 = _cairo_fixed_from_26_6 (control2->x); + y1 = _cairo_fixed_from_26_6 (control2->y); - p2.x = _cairo_fixed_from_26_6 (to->x); - p2.y = _cairo_fixed_from_26_6 (to->y); + x2 = _cairo_fixed_from_26_6 (to->x); + y2 = _cairo_fixed_from_26_6 (to->y); - _cairo_path_curve_to (path, &p0, &p1, &p2); + _cairo_path_fixed_curve_to (path, + x0, y0, + x1, y1, + x2, y2); return 0; } static cairo_status_t -_cairo_ft_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_ft_scaled_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) { int i; - cairo_ft_font_t *font = abstract_font; + cairo_ft_scaled_font_t *scaled_font = abstract_font; FT_GlyphSlot glyph; FT_Face face; FT_Error error; @@ -1148,7 +1284,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, /* delta */ }; - face = cairo_ft_font_lock_face (abstract_font); + face = cairo_ft_scaled_font_lock_face (abstract_font); if (!face) return CAIRO_STATUS_NO_MEMORY; @@ -1161,7 +1297,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, DOUBLE_TO_16_16 (-1.0), }; - error = FT_Load_Glyph (font->unscaled->face, glyphs[i].index, font->load_flags | FT_LOAD_NO_BITMAP); + error = FT_Load_Glyph (scaled_font->unscaled->face, glyphs[i].index, scaled_font->load_flags | FT_LOAD_NO_BITMAP); /* XXX: What to do in this error case? */ if (error) continue; @@ -1176,148 +1312,136 @@ _cairo_ft_font_glyph_path (void *abstract_font, DOUBLE_TO_26_6(glyphs[i].y)); FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); } - _cairo_path_close_path (path); + _cairo_path_fixed_close_path (path); - cairo_ft_font_unlock_face (abstract_font); + cairo_ft_scaled_font_unlock_face (abstract_font); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_create_glyph (cairo_image_glyph_cache_entry_t *val) -{ - ft_unscaled_font_t *unscaled = (ft_unscaled_font_t *)val->key.unscaled; - FT_GlyphSlot glyphslot; - unsigned int width, height, stride; - FT_Face face; - FT_Outline *outline; - FT_BBox cbox; - FT_Bitmap bitmap; - FT_Glyph_Metrics *metrics; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - glyphslot = unscaled->face->glyph; - metrics = &glyphslot->metrics; +const cairo_scaled_font_backend_t cairo_ft_scaled_font_backend = { + _cairo_ft_scaled_font_create, + _cairo_ft_scaled_font_destroy, + _cairo_ft_scaled_font_font_extents, + _cairo_ft_scaled_font_text_to_glyphs, + _cairo_ft_scaled_font_glyph_extents, + _cairo_ft_scaled_font_glyph_bbox, + _cairo_ft_scaled_font_show_glyphs, + _cairo_ft_scaled_font_glyph_path, + _cairo_ft_scaled_font_get_glyph_cache_key, +}; - face = _ft_unscaled_font_lock_face (unscaled); - if (!face) - return CAIRO_STATUS_NO_MEMORY; +/* ft_font_face_t */ - _ft_unscaled_font_set_scale (unscaled, &val->key.scale); - - if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } +static void +_ft_font_face_destroy (void *abstract_face) +{ + ft_font_face_t *font_face = abstract_face; + + ft_font_face_t *tmp_face = NULL; + ft_font_face_t *last_face = NULL; - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinates of the bearing and advance need to be negated. + /* When destroying the face created by cairo_ft_font_face_create_for_ft_face, + * we have a special "zombie" state for the face when the unscaled font + * is still alive but there are no public references to the font face. * - * Scale metrics back to glyph space from the scaled glyph space returned - * by FreeType + * We go from: + * + * font_face ------> unscaled + * <-....weak....../ + * + * To: + * + * font_face <------- unscaled */ - val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; + if (font_face->unscaled && + font_face->unscaled->from_face && + font_face->unscaled->base.refcount > 1) { + cairo_font_face_reference (&font_face->base); + + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; + + return; + } + + if (font_face->unscaled) { + /* Remove face from linked list */ + for (tmp_face = font_face->unscaled->faces; tmp_face; tmp_face = tmp_face->next_face) { + if (tmp_face == font_face) { + if (last_face) + last_face->next_face = tmp_face->next_face; + else + font_face->unscaled->faces = tmp_face->next_face; + } + + last_face = tmp_face; + } - val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; - val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; + } +} - /* - * use untransformed advance values - * XXX uses horizontal advance only at present; - should provide FT_LOAD_VERTICAL_LAYOUT - */ +static cairo_status_t +_ft_font_face_create_font (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **scaled_font) +{ + ft_font_face_t *font_face = abstract_face; - val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; - val->extents.y_advance = 0 / unscaled->y_scale; - - outline = &glyphslot->outline; + *scaled_font = _ft_scaled_font_create (font_face->unscaled, + font_face->load_flags, + font_matrix, ctm); + if (*scaled_font) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_STATUS_NO_MEMORY; +} - FT_Outline_Get_CBox (outline, &cbox); +static const cairo_font_face_backend_t _ft_font_face_backend = { + _ft_font_face_destroy, + _ft_font_face_create_font, +}; - cbox.xMin &= -64; - cbox.yMin &= -64; - cbox.xMax = (cbox.xMax + 63) & -64; - cbox.yMax = (cbox.yMax + 63) & -64; - - width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); - height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); - stride = (width + 3) & -4; - - if (width * height == 0) - val->image = NULL; - else - { +static cairo_font_face_t * +_ft_font_face_create (ft_unscaled_font_t *unscaled, + int load_flags) +{ + ft_font_face_t *font_face; - bitmap.pixel_mode = ft_pixel_mode_grays; - bitmap.num_grays = 256; - bitmap.width = width; - bitmap.rows = height; - bitmap.pitch = stride; - bitmap.buffer = calloc (1, stride * height); - - if (bitmap.buffer == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; + /* Looked for an existing matching font face */ + for (font_face = unscaled->faces; font_face; font_face = font_face->next_face) { + if (font_face->load_flags == load_flags) { + cairo_font_face_reference (&font_face->base); + return &font_face->base; } - - FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); - - if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { - free (bitmap.buffer); - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - val->image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data ((char *) bitmap.buffer, - CAIRO_FORMAT_A8, - width, height, stride); - if (val->image == NULL) { - free (bitmap.buffer); - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - _cairo_image_surface_assume_ownership_of_data (val->image); } - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinate of the control box needs to be negated. - */ - - val->size.width = (unsigned short) width; - val->size.height = (unsigned short) height; - val->size.x = (short) (cbox.xMin >> 6); - val->size.y = - (short) (cbox.yMax >> 6); + /* No match found, create a new one */ + font_face = malloc (sizeof (ft_font_face_t)); + if (!font_face) + return NULL; - FAIL: - _ft_unscaled_font_unlock_face (unscaled); + font_face->unscaled = unscaled; + _cairo_unscaled_font_reference (&unscaled->base); - return status; -} + font_face->load_flags = load_flags; -const cairo_font_backend_t cairo_ft_font_backend = { - _cairo_ft_font_create, - _cairo_ft_font_destroy_font, - _cairo_ft_font_destroy_unscaled_font, - _cairo_ft_font_font_extents, - _cairo_ft_font_text_to_glyphs, - _cairo_ft_font_glyph_extents, - _cairo_ft_font_glyph_bbox, - _cairo_ft_font_show_glyphs, - _cairo_ft_font_glyph_path, - _cairo_ft_font_get_glyph_cache_key, - _cairo_ft_font_create_glyph -}; + font_face->next_face = unscaled->faces; + unscaled->faces = font_face; + + _cairo_font_face_init (&font_face->base, &_ft_font_face_backend); + + return &font_face->base; +} /* implement the platform-specific interface */ /** - * cairo_ft_font_create: + * cairo_ft_font_face_create_for_pattern: * @pattern: A fully resolved fontconfig * pattern. A pattern can be resolved, by, among other things, calling * FcConfigSubstitute(), FcDefaultSubstitute(), then @@ -1325,112 +1449,83 @@ const cairo_font_backend_t cairo_ft_font_backend = { * pattern, so you should not further modify the pattern, but you can * release your reference to the pattern with FcPatternDestroy() if * you no longer need to access it. - * @scale: The scale at which this font will be used. The - * scale is given by multiplying the font matrix (see - * cairo_transform_font()) by the current transformation matrix. - * The translation elements of the resulting matrix are ignored. * - * Creates a new font for the FreeType font backend based on a + * Creates a new font face for the FreeType font backend based on a * fontconfig pattern. This font can then be used with - * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend - * specific functions like cairo_ft_font_lock_face(). + * cairo_set_font_face() or cairo_font_create(). The #cairo_scaled_font_t + * returned from cairo_font_create() is also for the FreeType backend + * and can be used with functions such as cairo_ft_font_lock_face(). * - * Return value: a newly created #cairo_font_t. Free with - * cairo_font_destroy() when you are done using it. + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. **/ -cairo_font_t * -cairo_ft_font_create (FcPattern *pattern, - cairo_matrix_t *scale) -{ - cairo_font_scale_t sc; - double tx, ty; +cairo_font_face_t * +cairo_ft_font_face_create_for_pattern (FcPattern *pattern) +{ + ft_unscaled_font_t *unscaled; + cairo_font_face_t *font_face; - cairo_matrix_get_affine (scale, - &sc.matrix[0][0], &sc.matrix[0][1], - &sc.matrix[1][0], &sc.matrix[1][1], - &tx, &ty); + unscaled = _ft_unscaled_font_get_for_pattern (pattern); + if (unscaled == NULL) + return NULL; - return _ft_font_create (pattern, &sc); + font_face = _ft_font_face_create (unscaled, _get_load_flags (pattern)); + _cairo_unscaled_font_destroy (&unscaled->base); + + return font_face; } /** * cairo_ft_font_create_for_ft_face: * @face: A FreeType face object, already opened. This must - * be kept around until the font object's refcount drops to - * zero and it is freed. The font object can be kept alive by - * internal caching, so it's safest to keep the face object - * around forever. + * be kept around until the face's refcount drops to + * zero and it is freed. Since the face may be referenced + * internally to Cairo, the best way to determine when it + * is safe to free the face is to pass a + * #cairo_destroy_func_t to cairo_font_face_set_user_data() * @load_flags: The flags to pass to FT_Load_Glyph when loading * glyphs from the font. These flags control aspects of * rendering such as hinting and antialiasing. See the FreeType * docs for full information. - * @scale: The scale at which this font will be used. The - * scale is given by multiplying the font matrix (see - * cairo_transform_font()) by the current transformation matrix. - * The translation elements of the resulting matrix are ignored. * - * Creates a new font forthe FreeType font backend from a pre-opened - * FreeType face. This font can then be used with cairo_set_font(), - * cairo_font_glyph_extents(), or FreeType backend specific - * functions like cairo_ft_font_lock_face() Cairo will determine the - * pixel size and transformation from the @scale parameter and call - * FT_Set_Transform() and FT_Set_Pixel_Sizes(). + * Creates a new font face for the FreeType font backend from a pre-opened + * FreeType face. This font can then be used with + * cairo_set_font_face() or cairo_font_create(). The #cairo_scaled_font_t + * returned from cairo_font_create() is also for the FreeType backend + * and can be used with functions such as cairo_ft_font_lock_face(). * - * Return value: a newly created #cairo_font_t. Free with - * cairo_font_destroy() when you are done using it. + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. **/ -cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face, - int load_flags, - cairo_matrix_t *scale) +cairo_font_face_t * +cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags) { - cairo_ft_font_t *f = NULL; - ft_unscaled_font_t *unscaled = NULL; - cairo_font_scale_t sc; - double tx, ty; + ft_unscaled_font_t *unscaled; + cairo_font_face_t *font_face; unscaled = _ft_unscaled_font_create_from_face (face); if (unscaled == NULL) return NULL; - f = malloc (sizeof(cairo_ft_font_t)); - if (f == NULL) - goto FREE_UNSCALED; - - f->unscaled = unscaled; - f->pattern = NULL; - f->load_flags = load_flags; - - cairo_matrix_get_affine (scale, - &sc.matrix[0][0], &sc.matrix[0][1], - &sc.matrix[1][0], &sc.matrix[1][1], - &tx, &ty); - - _cairo_font_init ((cairo_font_t *)f, &sc, &cairo_ft_font_backend); - - return (cairo_font_t *)f; - - FREE_UNSCALED: - _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); + font_face = _ft_font_face_create (unscaled, load_flags); + _cairo_unscaled_font_destroy (&unscaled->base); - return NULL; + return font_face; } - /** - * cairo_ft_font_lock_face: - * @ft_font: A #cairo_font_t from the FreeType font backend. Such an - * object can be created with cairo_ft_font_create() or - * cairo_ft_font_create_for_ft_face(). On some platforms the font from - * cairo_current_font() will also be a FreeType font, but using this - * functionality with fonts you don't create yourself is not - * recommended. + * cairo_ft_scaled_font_lock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_face()). * * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType * backend font and scales it appropriately for the font. You must * release the face with cairo_ft_font_unlock_face() * when you are done using it. Since the #FT_Face object can be - * shared between multiple #cairo_font_t objects, you must not + * shared between multiple #cairo_scaled_font_t objects, you must not * lock any other font objects until you unlock this one. A count is * kept of the number of times cairo_ft_font_lock_face() is * called. cairo_ft_font_unlock_face() must be called the same number @@ -1447,67 +1542,36 @@ cairo_ft_font_create_for_ft_face (FT_Face face, * Return value: The #FT_Face object for @font, scaled appropriately. **/ FT_Face -cairo_ft_font_lock_face (cairo_font_t *abstract_font) +cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; FT_Face face; - face = _ft_unscaled_font_lock_face (font->unscaled); + face = _ft_unscaled_font_lock_face (scaled_font->unscaled); if (!face) return NULL; - _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); + _ft_unscaled_font_set_scale (scaled_font->unscaled, &scaled_font->base.scale); return face; } /** - * cairo_ft_font_unlock_face: - * @ft_font: A #cairo_font_t from the FreeType font backend. Such an - * object can be created with cairo_ft_font_create() or - * cairo_ft_font_create_for_ft_face(). On some platforms the font from - * cairo_current_font() will also be a FreeType font, but using this - * functionality with fonts you don't create yourself is not - * recommended. + * cairo_ft_scaled_font_unlock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * cairo_ft_font_face_create_for_face()). * * Releases a face obtained with cairo_ft_font_lock_face(). See the * documentation for that function for full details. **/ void -cairo_ft_font_unlock_face (cairo_font_t *abstract_font) -{ - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; - - _ft_unscaled_font_unlock_face (font->unscaled); -} - -/** - * cairo_ft_font_get_pattern: - * @ft_font: A #cairo_font_t from the FreeType font backend. Such an - * object can be created with cairo_ft_font_create() or - * cairo_ft_font_create_for_ft_face(). On some platforms the font from - * cairo_current_font() will also be a FreeType font, but using this - * functionality with fonts you don't create yourself is not - * recommended. - * - * cairo_ft_font_get_pattern() gets the #FcPattern for a FreeType - * backend font. - - * Return value: The #FcPattenr for @font. The return value is owned - * by the font, so you must not modify it, and must call - * FcPatternReference() to keep a persistant reference to the - * pattern. If the font was created with cairo_ft_font_create_for_ft_face() - * returns %NULL. - **/ -FcPattern * -cairo_ft_font_get_pattern (cairo_font_t *abstract_font) +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; - - if (font == NULL) - return NULL; + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; - return font->pattern; + _ft_unscaled_font_unlock_face (scaled_font->unscaled); } /* We expose our unscaled font implementation internally for the the @@ -1515,11 +1579,11 @@ cairo_ft_font_get_pattern (cairo_font_t *abstract_font) * fonts-on-disk used by a document, so it can embed them. */ cairo_unscaled_font_t * -_cairo_ft_font_get_unscaled_font (cairo_font_t *abstract_font) +_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; - return (cairo_unscaled_font_t *)font->unscaled; + return &scaled_font->unscaled->base; } /* This differs from _cairo_ft_scaled_font_lock_face in that it doesn't diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h index 37a6feecc..494d8369a 100644 --- a/src/cairo-ft-private.h +++ b/src/cairo-ft-private.h @@ -40,7 +40,7 @@ #include <cairo-ft.h> #include <cairoint.h> -#ifdef CAIRO_HAS_FT_FONT +#if CAIRO_HAS_FT_FONT CAIRO_BEGIN_DECLS @@ -48,7 +48,7 @@ CAIRO_BEGIN_DECLS * the different fonts-on-disk used by a document, so it can embed them */ cairo_private cairo_unscaled_font_t * -_cairo_ft_font_get_unscaled_font (cairo_font_t *font); +_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *scaled_font); cairo_private FT_Face _cairo_ft_unscaled_font_lock_face (cairo_unscaled_font_t *unscaled_font); diff --git a/src/cairo-ft.h b/src/cairo-ft.h index f10c67d80..4e8b8bcdb 100644 --- a/src/cairo-ft.h +++ b/src/cairo-ft.h @@ -39,7 +39,7 @@ #include <cairo.h> -#ifdef CAIRO_HAS_FT_FONT +#if CAIRO_HAS_FT_FONT /* Fontconfig/Freetype platform-specific font interface */ @@ -49,25 +49,23 @@ CAIRO_BEGIN_DECLS -cairo_font_t * -cairo_ft_font_create (FcPattern *pattern, - cairo_matrix_t *scale); +cairo_font_face_t * +cairo_ft_font_face_create_for_pattern (FcPattern *pattern); -cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face, - int load_flags, - cairo_matrix_t *scale); +cairo_font_face_t * +cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags); FT_Face -cairo_ft_font_lock_face (cairo_font_t *ft_font); +cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font); void -cairo_ft_font_unlock_face (cairo_font_t *ft_font); - -FcPattern * -cairo_ft_font_get_pattern (cairo_font_t *ft_font); +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); CAIRO_END_DECLS +#else /* CAIRO_HAS_FT_FONT */ +# error Cairo was not compiled with support for the freetype font backend #endif /* CAIRO_HAS_FT_FONT */ + #endif /* CAIRO_FT_H */ diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c index ee664e1cc..673b972c3 100644 --- a/src/cairo-glitz-surface.c +++ b/src/cairo-glitz-surface.c @@ -27,25 +27,6 @@ #include "cairoint.h" #include "cairo-glitz.h" -void -cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface) -{ - cairo_surface_t *crsurface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - crsurface = cairo_glitz_surface_create (surface); - if (crsurface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, crsurface); - - cairo_surface_destroy (crsurface); -} - typedef struct _cairo_glitz_surface { cairo_surface_t base; @@ -54,8 +35,8 @@ typedef struct _cairo_glitz_surface { pixman_region16_t *clip; } cairo_glitz_surface_t; -static void -_cairo_glitz_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_glitz_surface_finish (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; @@ -66,7 +47,8 @@ _cairo_glitz_surface_destroy (void *abstract_surface) } glitz_surface_destroy (surface->surface); - free (surface); + + return CAIRO_STATUS_SUCCESS; } static glitz_format_name_t @@ -115,12 +97,6 @@ _cairo_glitz_surface_create_similar (void *abstract_src, return crsurface; } -static double -_cairo_glitz_surface_pixels_per_inch (void *abstract_surface) -{ - return 96.0; -} - static cairo_status_t _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, cairo_rectangle_t *interest, @@ -130,7 +106,7 @@ _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, cairo_image_surface_t *image; int x1, y1, x2, y2; int width, height; - char *pixels; + unsigned char *pixels; cairo_format_masks_t format; glitz_buffer_t *buffer; glitz_pixel_format_t pf; @@ -402,17 +378,17 @@ _cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, { glitz_transform_t transform; - transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + transform.matrix[0][0] = _cairo_fixed_from_double (matrix->xx); + transform.matrix[0][1] = _cairo_fixed_from_double (matrix->xy); + transform.matrix[0][2] = _cairo_fixed_from_double (matrix->x0); - transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); + transform.matrix[1][0] = _cairo_fixed_from_double (matrix->yx); + transform.matrix[1][1] = _cairo_fixed_from_double (matrix->yy); + transform.matrix[1][2] = _cairo_fixed_from_double (matrix->y0); transform.matrix[2][0] = 0; transform.matrix[2][1] = 0; - transform.matrix[2][2] = 1 << 16; + transform.matrix[2][2] = _cairo_fixed_from_double (1); glitz_surface_set_transform (surface->surface, &transform); } @@ -423,32 +399,49 @@ _glitz_operator (cairo_operator_t op) switch (op) { case CAIRO_OPERATOR_CLEAR: return GLITZ_OPERATOR_CLEAR; - case CAIRO_OPERATOR_SRC: + + case CAIRO_OPERATOR_SOURCE: return GLITZ_OPERATOR_SRC; - case CAIRO_OPERATOR_DST: - return GLITZ_OPERATOR_DST; - case CAIRO_OPERATOR_OVER_REVERSE: - return GLITZ_OPERATOR_OVER_REVERSE; + case CAIRO_OPERATOR_OVER: + return GLITZ_OPERATOR_OVER; case CAIRO_OPERATOR_IN: return GLITZ_OPERATOR_IN; - case CAIRO_OPERATOR_IN_REVERSE: - return GLITZ_OPERATOR_IN_REVERSE; case CAIRO_OPERATOR_OUT: return GLITZ_OPERATOR_OUT; - case CAIRO_OPERATOR_OUT_REVERSE: - return GLITZ_OPERATOR_OUT_REVERSE; case CAIRO_OPERATOR_ATOP: return GLITZ_OPERATOR_ATOP; - case CAIRO_OPERATOR_ATOP_REVERSE: + + case CAIRO_OPERATOR_DEST: + return GLITZ_OPERATOR_DST; + case CAIRO_OPERATOR_DEST_OVER: + return GLITZ_OPERATOR_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return GLITZ_OPERATOR_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return GLITZ_OPERATOR_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: return GLITZ_OPERATOR_ATOP_REVERSE; + case CAIRO_OPERATOR_XOR: return GLITZ_OPERATOR_XOR; case CAIRO_OPERATOR_ADD: return GLITZ_OPERATOR_ADD; - case CAIRO_OPERATOR_OVER: - default: + case CAIRO_OPERATOR_SATURATE: + /* XXX: OVER is definitely not the right thing here, (but it + * is what the original glitz backend code has always + * done). Cairo's SATURATE operator is the native GL + * compositing mode, (from my understanding). So why isn't + * there a GLITZ_OPERATOR_SATURATE for us to use here? */ return GLITZ_OPERATOR_OVER; } + + ASSERT_NOT_REACHED; + + /* Something's very broken if this line of code can be reached, so + we want to return something that would give a noticeably + incorrect result. The XOR operator seems so rearely desired + that it should fit the bill here. */ + return CAIRO_OPERATOR_XOR; } static glitz_status_t @@ -586,12 +579,12 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, pattern->filter != CAIRO_FILTER_BEST) break; - alpha = (gradient->stops[0].color.alpha * pattern->alpha) * 0xffff; + alpha = (gradient->stops[0].color.alpha) * 0xffff; for (i = 1; i < gradient->n_stops; i++) { unsigned short a; - a = (gradient->stops[i].color.alpha * pattern->alpha) * 0xffff; + a = (gradient->stops[i].color.alpha) * 0xffff; if (a != alpha) break; } @@ -733,7 +726,7 @@ _cairo_glitz_pattern_release_surface (cairo_glitz_surface_t *dst, _cairo_pattern_release_surface (&dst->base, &surface->base, &attr->base); else - _cairo_glitz_surface_destroy (surface); + cairo_surface_destroy (&surface->base); } static cairo_int_status_t @@ -753,50 +746,30 @@ _cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, { cairo_int_status_t status; cairo_pattern_union_t tmp; - cairo_bool_t src_opaque, mask_opaque; - double src_alpha, mask_alpha; - src_opaque = _cairo_pattern_is_opaque (src); - mask_opaque = !mask || _cairo_pattern_is_opaque (mask); - - /* For surface patterns, we move any translucency from src->alpha - * to mask->alpha so we can use the source unchanged. Otherwise we - * move the translucency from mask->alpha to src->alpha so that - * we can drop the mask if possible. - */ - if (src->type == CAIRO_PATTERN_SURFACE) - { - if (mask) { - mask_opaque = mask_opaque && src_opaque; - mask_alpha = mask->alpha * src->alpha; - } else { - mask_opaque = src_opaque; - mask_alpha = src->alpha; - } - - src_alpha = 1.0; - src_opaque = TRUE; - } - else + /* If src and mask are both solid, then the mask alpha can be + * combined into src and mask can be ignored. */ + + /* XXX: This optimization assumes that there is no color + * information in mask, so this will need to change when we + * support RENDER-style 4-channel masks. */ + + if (src->type == CAIRO_PATTERN_SOLID && + mask->type == CAIRO_PATTERN_SOLID) { - if (mask) - { - src_opaque = mask_opaque && src_opaque; - src_alpha = mask->alpha * src->alpha; - /* FIXME: This needs changing when we support RENDER - * style 4-channel masks. - */ - if (mask->type == CAIRO_PATTERN_SOLID) - mask = NULL; - } else - src_alpha = src->alpha; + cairo_color_t combined; + cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; + cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; - mask_alpha = 1.0; - mask_opaque = TRUE; - } + combined = src_solid->color; + _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - _cairo_pattern_init_copy (&tmp.base, src); - _cairo_pattern_set_alpha (&tmp.base, src_alpha); + _cairo_pattern_init_solid (&tmp.solid, &combined); + + mask = NULL; + } else { + _cairo_pattern_init_copy (&tmp.base, src); + } status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, src_x, src_y, @@ -808,14 +781,9 @@ _cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, if (status) return status; - if (mask || !mask_opaque) + if (mask) { - if (mask) - _cairo_pattern_init_copy (&tmp.base, mask); - else - _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); - - _cairo_pattern_set_alpha (&tmp.base, mask_alpha); + _cairo_pattern_init_copy (&tmp.base, mask); status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, mask_x, mask_y, @@ -937,7 +905,7 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, { cairo_glitz_surface_t *dst = abstract_dst; - if (op == CAIRO_OPERATOR_SRC) + if (op == CAIRO_OPERATOR_SOURCE) { glitz_color_t glitz_color; @@ -969,6 +937,8 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, (cairo_color_t *) color); if (!src) return CAIRO_STATUS_NO_MEMORY; + + glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); while (n_rects--) { @@ -1025,7 +995,6 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_union_t tmp; _cairo_pattern_init_copy (&tmp.base, pattern); - _cairo_pattern_set_alpha (&tmp.base, 1.0); status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, src_x, src_y, @@ -1033,8 +1002,6 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, &src, &attributes); _cairo_pattern_fini (&tmp.base); - - alpha = pattern->alpha * 0xffff; } else { @@ -1042,8 +1009,8 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, src_x, src_y, width, height, &src, &attributes); - alpha = 0xffff; } + alpha = 0xffff; if (status) return status; @@ -1076,7 +1043,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; } - color.red = color.green = color.blue = color.alpha = alpha; + color.red = color.green = color.blue = color.alpha = 0xffff; glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); @@ -1135,7 +1102,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, else { cairo_image_surface_t *image; - char *ptr; + unsigned char *ptr; int stride; stride = (width + 3) & -4; @@ -1149,7 +1116,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, memset (data, 0, stride * height); /* using negative stride */ - ptr = (char *) data + stride * (height - 1); + ptr = (unsigned char *) data + stride * (height - 1); image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (ptr, @@ -1166,18 +1133,6 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, (pixman_trapezoid_t *) traps, n_traps); - if (alpha != 0xffff) - { - pixman_color_t color; - - color.red = color.green = color.blue = color.alpha = alpha; - - pixman_fill_rectangle (PIXMAN_OPERATOR_IN, - image->pixman_image, - &color, - 0, 0, width, height); - } - mask = (cairo_glitz_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, CAIRO_FORMAT_A8, 0, @@ -1228,18 +1183,6 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, } static cairo_int_status_t -_cairo_glitz_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_glitz_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t _cairo_glitz_surface_set_clip_region (void *abstract_surface, pixman_region16_t *region) { @@ -1275,10 +1218,902 @@ _cairo_glitz_surface_set_clip_region (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_glitz_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = glitz_surface_get_width (surface->surface); + rectangle->height = glitz_surface_get_height (surface->surface); + + return CAIRO_STATUS_SUCCESS; +} + +#define CAIRO_GLITZ_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 + +#define CAIRO_GLITZ_AREA_AVAILABLE 0 +#define CAIRO_GLITZ_AREA_DIVIDED 1 +#define CAIRO_GLITZ_AREA_OCCUPIED 2 + +typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t; + +typedef struct _cairo_glitz_area { + int state; + int level; + int x, y; + int width, height; + struct _cairo_glitz_area *area[4]; + cairo_glitz_root_area_t *root; + void *closure; +} cairo_glitz_area_t; + +static cairo_glitz_area_t _empty_area = { + 0, 0, 0, 0, 0, 0, + { NULL, NULL, NULL, NULL }, + NULL, + NULL +}; + +typedef struct _cairo_glitz_area_funcs { + cairo_status_t (*move_in) (cairo_glitz_area_t *area, + void *closure); + + void (*move_out) (cairo_glitz_area_t *area, + void *closure); + + int (*compare_score) (cairo_glitz_area_t *area, + void *closure1, + void *closure2); +} cairo_glitz_area_funcs_t; + +struct _cairo_glitz_root_area { + int max_level; + int width, height; + cairo_glitz_area_t *area; + const cairo_glitz_area_funcs_t *funcs; +}; + +static cairo_status_t +_cairo_glitz_area_move_in (cairo_glitz_area_t *area, + void *closure) +{ + area->closure = closure; + area->state = CAIRO_GLITZ_AREA_OCCUPIED; + + return (*area->root->funcs->move_in) (area, area->closure); +} + +static void +_cairo_glitz_area_move_out (cairo_glitz_area_t *area) +{ + (*area->root->funcs->move_out) (area, area->closure); + + area->closure = NULL; + area->state = CAIRO_GLITZ_AREA_AVAILABLE; +} + +static cairo_glitz_area_t * +_cairo_glitz_area_create (cairo_glitz_root_area_t *root, + int level, + int x, + int y, + int width, + int height) +{ + cairo_glitz_area_t *area; + int n = 4; + + area = malloc (sizeof (cairo_glitz_area_t)); + if (!area) + return NULL; + + area->level = level; + area->x = x; + area->y = y; + area->width = width; + area->height = height; + area->root = root; + area->closure = NULL; + area->state = CAIRO_GLITZ_AREA_AVAILABLE; + + while (n--) + area->area[n] = NULL; + + return area; +} + +static void +_cairo_glitz_area_destroy (cairo_glitz_area_t *area) +{ + if (!area) + return; + + if (area->state == CAIRO_GLITZ_AREA_OCCUPIED) + { + _cairo_glitz_area_move_out (area); + } + else + { + int n = 4; + + while (n--) + _cairo_glitz_area_destroy (area->area[n]); + } + + free (area); +} + +static cairo_glitz_area_t * +_cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area) +{ + if (!area) + return NULL; + + switch (area->state) { + case CAIRO_GLITZ_AREA_OCCUPIED: + return area; + case CAIRO_GLITZ_AREA_AVAILABLE: + break; + case CAIRO_GLITZ_AREA_DIVIDED: { + cairo_glitz_area_t *tmp, *top = NULL; + int i; + + for (i = 0; i < 4; i++) + { + tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]); + if (tmp && top) + { + if ((*area->root->funcs->compare_score) (tmp, + tmp->closure, + top->closure) > 0) + top = tmp; + } + else if (tmp) + { + top = tmp; + } + } + return top; + } + } + + return NULL; +} + +static cairo_int_status_t +_cairo_glitz_area_find (cairo_glitz_area_t *area, + int width, + int height, + cairo_bool_t kick_out, + void *closure) +{ + if (area->width < width || area->height < height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + switch (area->state) { + case CAIRO_GLITZ_AREA_OCCUPIED: + if (kick_out) + { + if ((*area->root->funcs->compare_score) (area, + area->closure, + closure) >= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_glitz_area_move_out (area); + } else + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* fall-through */ + case CAIRO_GLITZ_AREA_AVAILABLE: { + if (area->level == area->root->max_level || + (area->width == width && area->height == height)) + { + return _cairo_glitz_area_move_in (area, closure); + } + else + { + int dx[4], dy[4], w[4], h[4], i; + + dx[0] = dx[2] = dy[0] = dy[1] = 0; + + w[0] = w[2] = dx[1] = dx[3] = width; + h[0] = h[1] = dy[2] = dy[3] = height; + + w[1] = w[3] = area->width - width; + h[2] = h[3] = area->height - height; + + for (i = 0; i < 2; i++) + { + if (w[i]) + area->area[i] = + _cairo_glitz_area_create (area->root, + area->level + 1, + area->x + dx[i], + area->y + dy[i], + w[i], h[i]); + } + + for (; i < 4; i++) + { + if (w[i] && h[i]) + area->area[i] = + _cairo_glitz_area_create (area->root, + area->level + 1, + area->x + dx[i], + area->y + dy[i], + w[i], h[i]); + } + + area->state = CAIRO_GLITZ_AREA_DIVIDED; + + if (CAIRO_OK (_cairo_glitz_area_find (area->area[0], + width, height, + kick_out, closure))) + return CAIRO_STATUS_SUCCESS; + } + } break; + case CAIRO_GLITZ_AREA_DIVIDED: { + cairo_glitz_area_t *to_area; + int i, rejected = FALSE; + + for (i = 0; i < 4; i++) + { + if (area->area[i]) + { + if (area->area[i]->width >= width && + area->area[i]->height >= height) + { + if (CAIRO_OK (_cairo_glitz_area_find (area->area[i], + width, height, + kick_out, closure))) + return CAIRO_STATUS_SUCCESS; + + rejected = TRUE; + } + } + } + + if (rejected) + return CAIRO_INT_STATUS_UNSUPPORTED; + + to_area = _cairo_glitz_area_get_top_scored_sub_area (area); + if (to_area) + { + if (kick_out) + { + if ((*area->root->funcs->compare_score) (to_area, + to_area->closure, + closure) >= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + } else + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + for (i = 0; i < 4; i++) + { + _cairo_glitz_area_destroy (area->area[i]); + area->area[i] = NULL; + } + + area->closure = NULL; + area->state = CAIRO_GLITZ_AREA_AVAILABLE; + if (CAIRO_OK (_cairo_glitz_area_find (area, width, height, + TRUE, closure))) + return CAIRO_STATUS_SUCCESS; + + } break; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_glitz_root_area_init (cairo_glitz_root_area_t *root, + int max_level, + int width, + int height, + const cairo_glitz_area_funcs_t *funcs) +{ + root->max_level = max_level; + root->funcs = funcs; + + root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height); + if (!root->area) + return CAIRO_STATUS_NO_MEMORY; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root) +{ + _cairo_glitz_area_destroy (root->area); +} + +#define GLYPH_CACHE_TEXTURE_SIZE 512 +#define GLYPH_CACHE_MAX_LEVEL 64 +#define GLYPH_CACHE_MAX_HEIGHT 72 +#define GLYPH_CACHE_MAX_WIDTH 72 + +#define WRITE_VEC2(ptr, _x, _y) \ + *(ptr)++ = (_x); \ + *(ptr)++ = (_y) + +#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \ + WRITE_VEC2 (ptr, _vx1, _vy1); \ + WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \ + WRITE_VEC2 (ptr, _vx2, _vy1); \ + WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \ + WRITE_VEC2 (ptr, _vx2, _vy2); \ + WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \ + WRITE_VEC2 (ptr, _vx1, _vy2); \ + WRITE_VEC2 (ptr, (p1)->x, (p1)->y) + +typedef struct _cairo_glitz_glyph_cache { + cairo_cache_t base; + cairo_glitz_root_area_t root; + glitz_surface_t *surface; +} cairo_glitz_glyph_cache_t; + +typedef struct { + cairo_glyph_cache_key_t key; + int refcount; + cairo_glyph_size_t size; + cairo_glitz_area_t *area; + cairo_bool_t locked; + cairo_point_double_t p1, p2; +} cairo_glitz_glyph_cache_entry_t; + +static cairo_status_t +_cairo_glitz_glyph_move_in (cairo_glitz_area_t *area, + void *closure) +{ + cairo_glitz_glyph_cache_entry_t *entry = closure; + + entry->area = area; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_glyph_move_out (cairo_glitz_area_t *area, + void *closure) +{ + cairo_glitz_glyph_cache_entry_t *entry = closure; + + entry->area = NULL; +} + +static int +_cairo_glitz_glyph_compare (cairo_glitz_area_t *area, + void *closure1, + void *closure2) +{ + cairo_glitz_glyph_cache_entry_t *entry = closure1; + + if (entry->locked) + return 1; + + return -1; +} + +static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = { + _cairo_glitz_glyph_move_in, + _cairo_glitz_glyph_move_out, + _cairo_glitz_glyph_compare +}; + +static cairo_status_t +_cairo_glitz_glyph_cache_entry_create (void *abstract_cache, + void *abstract_key, + void **return_entry) +{ + cairo_glitz_glyph_cache_entry_t *entry; + cairo_glyph_cache_key_t *key = abstract_key; + + entry = malloc (sizeof (cairo_glitz_glyph_cache_entry_t)); + if (!entry) + return CAIRO_STATUS_NO_MEMORY; + + entry->refcount = 1; + entry->key = *key; + entry->area = NULL; + entry->locked = FALSE; + + _cairo_unscaled_font_reference (entry->key.unscaled); + + *return_entry = entry; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_glitz_glyph_cache_entry_destroy (void *abstract_cache, + void *abstract_entry) +{ + cairo_glitz_glyph_cache_entry_t *entry = abstract_entry; + + entry->refcount--; + if (entry->refcount) + return; + + if (entry->area) + _cairo_glitz_area_move_out (entry->area); + + _cairo_unscaled_font_destroy (entry->key.unscaled); + + free (entry); +} + +static void +_cairo_glitz_glyph_cache_entry_reference (void *abstract_entry) +{ + cairo_glitz_glyph_cache_entry_t *entry = abstract_entry; + + entry->refcount++; +} + +static void +_cairo_glitz_glyph_cache_destroy (void *abstract_cache) +{ + cairo_glitz_glyph_cache_t *cache = abstract_cache; + + _cairo_glitz_root_area_fini (&cache->root); + + glitz_surface_destroy (cache->surface); +} + +static const cairo_cache_backend_t _cairo_glitz_glyph_cache_backend = { + _cairo_glyph_cache_hash, + _cairo_glyph_cache_keys_equal, + _cairo_glitz_glyph_cache_entry_create, + _cairo_glitz_glyph_cache_entry_destroy, + _cairo_glitz_glyph_cache_destroy +}; + +static cairo_glitz_glyph_cache_t *_cairo_glitz_glyph_caches = NULL; + +static cairo_glitz_glyph_cache_t * +_cairo_glitz_get_glyph_cache (cairo_glitz_surface_t *surface) +{ + cairo_glitz_glyph_cache_t *cache; + glitz_drawable_t *drawable; + glitz_format_t *format; + + /* + * FIXME: caches should be thread specific, display specific and screen + * specific. + */ + + if (_cairo_glitz_glyph_caches) + return _cairo_glitz_glyph_caches; + + drawable = glitz_surface_get_drawable (surface->surface); + + format = glitz_find_standard_format (drawable, GLITZ_STANDARD_A8); + if (!format) + return NULL; + + cache = malloc (sizeof (cairo_glitz_glyph_cache_t)); + if (!cache) + return NULL; + + cache->surface = + glitz_surface_create (drawable, format, + GLYPH_CACHE_TEXTURE_SIZE, + GLYPH_CACHE_TEXTURE_SIZE, + 0, NULL); + if (!cache->surface) + { + free (cache); + return NULL; + } + + if (_cairo_glitz_root_area_init (&cache->root, + GLYPH_CACHE_MAX_LEVEL, + GLYPH_CACHE_TEXTURE_SIZE, + GLYPH_CACHE_TEXTURE_SIZE, + &_cairo_glitz_area_funcs)) + { + glitz_surface_destroy (cache->surface); + free (cache); + return NULL; + } + + if (_cairo_cache_init (&cache->base, + &_cairo_glitz_glyph_cache_backend, + CAIRO_GLITZ_GLYPH_CACHE_MEMORY_DEFAULT)) + { + _cairo_glitz_root_area_fini (&cache->root); + glitz_surface_destroy (cache->surface); + free (cache); + return NULL; + } + + _cairo_glitz_glyph_caches = cache; + + return cache; +} + +#define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536) + +static cairo_status_t +_cairo_glitz_cache_glyph (cairo_glitz_glyph_cache_t *cache, + cairo_glitz_glyph_cache_entry_t *entry, + cairo_image_glyph_cache_entry_t *image_entry) +{ + glitz_point_fixed_t p1, p2; + glitz_pixel_format_t pf; + glitz_buffer_t *buffer; + pixman_format_t *format; + int am, rm, gm, bm; + + entry->size = image_entry->size; + + if (entry->size.width > GLYPH_CACHE_MAX_WIDTH || + entry->size.height > GLYPH_CACHE_MAX_HEIGHT) + return CAIRO_STATUS_SUCCESS; + + if (!image_entry->image) + { + entry->area = &_empty_area; + return CAIRO_STATUS_SUCCESS; + } + + format = pixman_image_get_format (image_entry->image->pixman_image); + if (!format) + return CAIRO_STATUS_NO_MEMORY; + + if (_cairo_glitz_area_find (cache->root.area, + entry->size.width, + entry->size.height, + FALSE, entry)) + { + if (_cairo_glitz_area_find (cache->root.area, + entry->size.width, + entry->size.height, + TRUE, entry)) + return CAIRO_STATUS_SUCCESS; + } + + buffer = glitz_buffer_create_for_data (image_entry->image->data); + if (!buffer) + { + _cairo_glitz_area_move_out (entry->area); + return CAIRO_STATUS_NO_MEMORY; + } + + pixman_format_get_masks (format, &pf.masks.bpp, &am, &rm, &gm, &bm); + + pf.masks.alpha_mask = am; + pf.masks.red_mask = rm; + pf.masks.green_mask = gm; + pf.masks.blue_mask = bm; + + pf.bytes_per_line = image_entry->image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; + pf.xoffset = 0; + pf.skip_lines = 0; + + glitz_set_pixels (cache->surface, + entry->area->x, + entry->area->y, + entry->size.width, + entry->size.height, + &pf, buffer); + + glitz_buffer_destroy (buffer); + + p1.x = entry->area->x << 16; + p1.y = entry->area->y << 16; + p2.x = (entry->area->x + entry->size.width) << 16; + p2.y = (entry->area->y + entry->size.height) << 16; + + glitz_surface_translate_point (cache->surface, &p1, &p1); + glitz_surface_translate_point (cache->surface, &p2, &p2); + + entry->p1.x = FIXED_TO_FLOAT (p1.x); + entry->p1.y = FIXED_TO_FLOAT (p1.y); + entry->p2.x = FIXED_TO_FLOAT (p2.x); + entry->p2.y = FIXED_TO_FLOAT (p2.y); + + return CAIRO_STATUS_SUCCESS; +} + +#define N_STACK_BUF 256 + +static cairo_int_status_t +_cairo_glitz_surface_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + cairo_pattern_t *pattern, + void *abstract_surface, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_glitz_surface_attributes_t attributes; + cairo_glitz_surface_t *dst = abstract_surface; + cairo_glitz_surface_t *src; + cairo_glitz_glyph_cache_t *cache; + cairo_glitz_glyph_cache_entry_t *stack_entries[N_STACK_BUF]; + cairo_glitz_glyph_cache_entry_t **entries; + cairo_glyph_cache_key_t key; + glitz_float_t stack_vertices[N_STACK_BUF * 16]; + glitz_float_t *vertices; + glitz_buffer_t *buffer; + cairo_int_status_t status; + int i, cached_glyphs = 0; + int remaining_glyps = num_glyphs; + glitz_float_t x1, y1, x2, y2; + static glitz_vertex_format_t format = { + GLITZ_PRIMITIVE_QUADS, + GLITZ_DATA_TYPE_FLOAT, + sizeof (glitz_float_t) * 4, + GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK, + { 0 }, + { + GLITZ_DATA_TYPE_FLOAT, + GLITZ_COORDINATE_SIZE_XY, + sizeof (glitz_float_t) * 2, + } + }; + + if (op == CAIRO_OPERATOR_SATURATE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, + src_x, src_y, + width, height, + &src, &attributes); + if (status) + return status; + + _cairo_glitz_surface_set_attributes (src, &attributes); + + if (num_glyphs > N_STACK_BUF) + { + char *data; + + data = malloc (num_glyphs * sizeof (void *) + + num_glyphs * sizeof (glitz_float_t) * 16); + if (!data) + goto FAIL1; + + entries = (cairo_glitz_glyph_cache_entry_t **) data; + vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *)); + } + else + { + entries = stack_entries; + vertices = stack_vertices; + } + + buffer = glitz_buffer_create_for_data (vertices); + if (!buffer) + goto FAIL2; + + cache = _cairo_glitz_get_glyph_cache (dst); + if (!cache) + { + num_glyphs = 0; + goto UNLOCK; + } + + _cairo_scaled_font_get_glyph_cache_key (scaled_font, &key); + + for (i = 0; i < num_glyphs; i++) + { + key.index = glyphs[i].index; + + status = _cairo_cache_lookup (&cache->base, + &key, + (void **) &entries[i], + NULL); + if (status) + { + num_glyphs = i; + goto UNLOCK; + } + + _cairo_glitz_glyph_cache_entry_reference (entries[i]); + + if (entries[i]->area) + { + remaining_glyps--; + + if (entries[i]->area->width) + { + x1 = floor (glyphs[i].x + 0.5) + entries[i]->size.x; + y1 = floor (glyphs[i].y + 0.5) + entries[i]->size.y; + x2 = x1 + entries[i]->size.width; + y2 = y1 + entries[i]->size.height; + + WRITE_BOX (vertices, x1, y1, x2, y2, + &entries[i]->p1, &entries[i]->p2); + + entries[i]->locked = TRUE; + cached_glyphs++; + } + } + } + + if (remaining_glyps) + { + cairo_cache_t *image_cache; + cairo_image_glyph_cache_entry_t *image_entry; + cairo_surface_t *image; + cairo_glitz_surface_t *clone; + + _cairo_lock_global_image_glyph_cache (); + + image_cache = _cairo_get_global_image_glyph_cache (); + if (!image_cache) + { + _cairo_unlock_global_image_glyph_cache (); + status = CAIRO_STATUS_NO_MEMORY; + goto UNLOCK; + } + + for (i = 0; i < num_glyphs; i++) + { + if (!entries[i]->area) + { + key.index = glyphs[i].index; + + status = _cairo_cache_lookup (image_cache, + &key, + (void **) &image_entry, + NULL); + if (status) + { + _cairo_unlock_global_image_glyph_cache (); + goto UNLOCK; + } + + status = _cairo_glitz_cache_glyph (cache, + entries[i], + image_entry); + if (status) + { + _cairo_unlock_global_image_glyph_cache (); + goto UNLOCK; + } + } + + x1 = floor (glyphs[i].x + 0.5); + y1 = floor (glyphs[i].y + 0.5); + + if (entries[i]->area) + { + if (entries[i]->area->width) + { + x1 += entries[i]->size.x; + y1 += entries[i]->size.y; + x2 = x1 + entries[i]->size.width; + y2 = y1 + entries[i]->size.height; + + WRITE_BOX (vertices, x1, y1, x2, y2, + &entries[i]->p1, &entries[i]->p2); + + entries[i]->locked = TRUE; + cached_glyphs++; + } + } + else + { + x1 += image_entry->size.x; + y1 += image_entry->size.y; + + if (!image_entry->image) + continue; + + image = &image_entry->image->base; + status = + _cairo_glitz_surface_clone_similar (abstract_surface, + image, + (cairo_surface_t **) + &clone); + if (status) + { + _cairo_unlock_global_image_glyph_cache (); + goto UNLOCK; + } + + glitz_composite (_glitz_operator (op), + src->surface, + clone->surface, + dst->surface, + src_x + attributes.base.x_offset + x1, + src_y + attributes.base.y_offset + y1, + 0, 0, + x1, y1, + image_entry->size.width, + image_entry->size.height); + + cairo_surface_destroy (&clone->base); + + if (glitz_surface_get_status (dst->surface) == + GLITZ_STATUS_NOT_SUPPORTED) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_unlock_global_image_glyph_cache (); + goto UNLOCK; + } + } + } + + _cairo_unlock_global_image_glyph_cache (); + } + + if (cached_glyphs) + { + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_VERTEX, + (glitz_geometry_format_t *) &format, + buffer); + + glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0); + + glitz_composite (_glitz_operator (op), + src->surface, + cache->surface, + dst->surface, + src_x + attributes.base.x_offset, + src_y + attributes.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_NONE, + NULL, NULL); + } + +UNLOCK: + if (cached_glyphs) + { + for (i = 0; i < num_glyphs; i++) + entries[i]->locked = FALSE; + } + + for (i = 0; i < num_glyphs; i++) + _cairo_glitz_glyph_cache_entry_destroy (cache, entries[i]); + + glitz_buffer_destroy (buffer); + + FAIL2: + if (num_glyphs > N_STACK_BUF) + free (entries); + + FAIL1: + if (attributes.n_params) + free (attributes.params); + + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + + if (status) + return status; + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_create_similar, - _cairo_glitz_surface_destroy, - _cairo_glitz_surface_pixels_per_inch, + _cairo_glitz_surface_finish, _cairo_glitz_surface_acquire_source_image, _cairo_glitz_surface_release_source_image, _cairo_glitz_surface_acquire_dest_image, @@ -1287,10 +2122,11 @@ static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_composite, _cairo_glitz_surface_fill_rectangles, _cairo_glitz_surface_composite_trapezoids, - _cairo_glitz_surface_copy_page, - _cairo_glitz_surface_show_page, + NULL, /* copy_page */ + NULL, /* show_page */ _cairo_glitz_surface_set_clip_region, - NULL /* show_glyphs */ + _cairo_glitz_surface_get_extents, + _cairo_glitz_surface_show_glyphs }; cairo_surface_t * diff --git a/src/cairo-glitz.h b/src/cairo-glitz.h index f1917eb28..f5b4f2815 100644 --- a/src/cairo-glitz.h +++ b/src/cairo-glitz.h @@ -39,20 +39,19 @@ #include <cairo.h> -#ifdef CAIRO_HAS_GLITZ_SURFACE +#if CAIRO_HAS_GLITZ_SURFACE #include <glitz.h> CAIRO_BEGIN_DECLS -void -cairo_set_target_glitz (cairo_t *cr, - glitz_surface_t *surface); - cairo_surface_t * cairo_glitz_surface_create (glitz_surface_t *surface); CAIRO_END_DECLS +#else /* CAIRO_HAS_GLITZ_SURFACE */ +# error Cairo was not compiled with support for the glitz backend #endif /* CAIRO_HAS_GLITZ_SURFACE */ + #endif /* CAIRO_GLITZ_H */ diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h new file mode 100644 index 000000000..eeb35b83e --- /dev/null +++ b/src/cairo-gstate-private.h @@ -0,0 +1,80 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#ifndef CAIRO_GSTATE_PRIVATE_H +#define CAIRO_GSTATE_PRIVATE_H + +struct _cairo_gstate { + cairo_operator_t operator; + + double tolerance; + + /* stroke style */ + double line_width; + cairo_line_cap_t line_cap; + cairo_line_join_t line_join; + double miter_limit; + + cairo_fill_rule_t fill_rule; + + double *dash; + int num_dashes; + double dash_offset; + + char *font_family; /* NULL means CAIRO_FONT_FAMILY_DEFAULT; */ + cairo_font_slant_t font_slant; + cairo_font_weight_t font_weight; + + cairo_font_face_t *font_face; + cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */ + + cairo_surface_t *surface; + int surface_level; /* Used to detect bad nested use */ + + cairo_pattern_t *source; + + cairo_clip_rec_t clip; + + cairo_matrix_t font_matrix; + + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + + cairo_pen_t pen_regular; + + struct _cairo_gstate *next; +}; + +#endif /* CAIRO_GSTATE_PRIVATE_H */ diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index d6db560a3..45c729fc9 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -35,10 +36,15 @@ */ #include <stdlib.h> -#include <math.h> #include "cairoint.h" +#include "cairo-gstate-private.h" + +static cairo_status_t +_cairo_gstate_set_target_surface (cairo_gstate_t *gstate, + cairo_surface_t *surface); + static cairo_status_t _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_pattern_t *src, @@ -49,11 +55,14 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, static cairo_status_t _cairo_gstate_ensure_font (cairo_gstate_t *gstate); +static cairo_status_t +_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate); + static void _cairo_gstate_unset_font (cairo_gstate_t *gstate); cairo_gstate_t * -_cairo_gstate_create () +_cairo_gstate_create (cairo_surface_t *target) { cairo_status_t status; cairo_gstate_t *gstate; @@ -62,7 +71,7 @@ _cairo_gstate_create () if (gstate) { - status = _cairo_gstate_init (gstate); + status = _cairo_gstate_init (gstate, target); if (status) { free (gstate); return NULL; @@ -73,8 +82,11 @@ _cairo_gstate_create () } cairo_status_t -_cairo_gstate_init (cairo_gstate_t *gstate) +_cairo_gstate_init (cairo_gstate_t *gstate, + cairo_surface_t *target) { + cairo_status_t status; + gstate->operator = CAIRO_GSTATE_OPERATOR_DEFAULT; gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; @@ -90,32 +102,33 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->num_dashes = 0; gstate->dash_offset = 0.0; - gstate->font_family = NULL; - gstate->font_slant = CAIRO_FONT_SLANT_DEFAULT; - gstate->font_weight = CAIRO_FONT_WEIGHT_DEFAULT; - - gstate->font = NULL; + gstate->scaled_font = NULL; + gstate->font_face = NULL; + cairo_matrix_init_scale (&gstate->font_matrix, + CAIRO_GSTATE_DEFAULT_FONT_SIZE, + CAIRO_GSTATE_DEFAULT_FONT_SIZE); + gstate->surface = NULL; + gstate->surface_level = 0; gstate->clip.region = NULL; gstate->clip.surface = NULL; - - gstate->pattern = _cairo_pattern_create_solid (0.0, 0.0, 0.0); - if (!gstate->pattern) - return CAIRO_STATUS_NO_MEMORY; - gstate->alpha = 1.0; - - gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; - _cairo_gstate_default_matrix (gstate); + gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); + if (!gstate->source) + return CAIRO_STATUS_NO_MEMORY; - _cairo_path_init (&gstate->path); + _cairo_gstate_identity_matrix (gstate); _cairo_pen_init_empty (&gstate->pen_regular); gstate->next = NULL; + status = _cairo_gstate_set_target_surface (gstate, target); + if (status) + return status; + return CAIRO_STATUS_SUCCESS; } @@ -138,51 +151,41 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) memcpy (gstate->dash, other->dash, other->num_dashes * sizeof (double)); } - if (other->font_family) { - gstate->font_family = strdup (other->font_family); - if (!gstate->font_family) - goto CLEANUP_DASH; - } - - if (other->font) { - gstate->font = other->font; - cairo_font_reference (gstate->font); - } - if (other->clip.region) { gstate->clip.region = pixman_region_create (); pixman_region_copy (gstate->clip.region, other->clip.region); } + if (gstate->font_face) + cairo_font_face_reference (gstate->font_face); + + if (gstate->scaled_font) + cairo_scaled_font_reference (gstate->scaled_font); + cairo_surface_reference (gstate->surface); cairo_surface_reference (gstate->clip.surface); - cairo_pattern_reference (gstate->pattern); + cairo_pattern_reference (gstate->source); - status = _cairo_path_init_copy (&gstate->path, &other->path); + status = _cairo_pen_init_copy (&gstate->pen_regular, &other->pen_regular); if (status) goto CLEANUP_FONT; - status = _cairo_pen_init_copy (&gstate->pen_regular, &other->pen_regular); + status = _cairo_surface_begin (gstate->surface); if (status) - goto CLEANUP_PATH; + goto CLEANUP_PEN; + gstate->surface_level = gstate->surface->level; return status; - CLEANUP_PATH: - _cairo_path_fini (&gstate->path); + CLEANUP_PEN: + _cairo_pen_fini (&gstate->pen_regular); CLEANUP_FONT: - cairo_font_destroy (gstate->font); - gstate->font = NULL; + cairo_scaled_font_destroy (gstate->scaled_font); + gstate->scaled_font = NULL; - if (gstate->font_family) { - free (gstate->font_family); - gstate->font_family = NULL; - } - - CLEANUP_DASH: free (gstate->dash); gstate->dash = NULL; @@ -192,15 +195,17 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) void _cairo_gstate_fini (cairo_gstate_t *gstate) { - if (gstate->font_family) - free (gstate->font_family); + if (gstate->font_face) + cairo_font_face_destroy (gstate->font_face); - if (gstate->font) - cairo_font_destroy (gstate->font); + if (gstate->scaled_font) + cairo_scaled_font_destroy (gstate->scaled_font); - if (gstate->surface) + if (gstate->surface) { + _cairo_surface_end (gstate->surface); cairo_surface_destroy (gstate->surface); - gstate->surface = NULL; + gstate->surface = NULL; + } if (gstate->clip.surface) cairo_surface_destroy (gstate->clip.surface); @@ -210,14 +215,7 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) pixman_region_destroy (gstate->clip.region); gstate->clip.region = NULL; - cairo_pattern_destroy (gstate->pattern); - - _cairo_matrix_fini (&gstate->font_matrix); - - _cairo_matrix_fini (&gstate->ctm); - _cairo_matrix_fini (&gstate->ctm_inverse); - - _cairo_path_fini (&gstate->path); + cairo_pattern_destroy (gstate->source); _cairo_pen_fini (&gstate->pen_regular); @@ -253,28 +251,12 @@ _cairo_gstate_clone (cairo_gstate_t *gstate) return clone; } -cairo_status_t -_cairo_gstate_copy (cairo_gstate_t *dest, cairo_gstate_t *src) -{ - cairo_status_t status; - cairo_gstate_t *next; - - /* Preserve next pointer over fini/init */ - next = dest->next; - _cairo_gstate_fini (dest); - status = _cairo_gstate_init_copy (dest, src); - dest->next = next; - - return status; -} - /* Push rendering off to an off-screen group. */ /* XXX: Rethinking this API cairo_status_t _cairo_gstate_begin_group (cairo_gstate_t *gstate) { Pixmap pix; - cairo_color_t clear; unsigned int width, height; gstate->parent_surface = gstate->surface; @@ -295,12 +277,9 @@ _cairo_gstate_begin_group (cairo_gstate_t *gstate) _cairo_surface_set_drawableWH (gstate->surface, pix, width, height); - _cairo_color_init (&clear); - _cairo_color_set_alpha (&clear, 0); - status = _cairo_surface_fill_rectangle (gstate->surface, - CAIRO_OPERATOR_SRC, - &clear, + CAIRO_OPERATOR_SOURCE, + &CAIRO_COLOR_TRANSPARENT, 0, 0, _cairo_surface_get_width (gstate->surface), _cairo_surface_get_height (gstate->surface)); @@ -325,7 +304,6 @@ _cairo_gstate_end_group (cairo_gstate_t *gstate) _cairo_surface_init (&mask, gstate->dpy); _cairo_color_init (&mask_color); - _cairo_color_set_alpha (&mask_color, gstate->alpha); _cairo_surface_set_solid_color (&mask, &mask_color); @@ -355,73 +333,93 @@ _cairo_gstate_end_group (cairo_gstate_t *gstate) } */ -cairo_status_t +static cairo_status_t _cairo_gstate_set_target_surface (cairo_gstate_t *gstate, cairo_surface_t *surface) { - double scale; + cairo_status_t status; + + if (gstate->surface == surface) + return CAIRO_STATUS_SUCCESS; + + if (surface) { + status = _cairo_surface_begin_reset_clip (surface); + if (!CAIRO_OK (status)) + return status; + } _cairo_gstate_unset_font (gstate); - if (gstate->surface) + if (gstate->surface) { + _cairo_surface_end (gstate->surface); cairo_surface_destroy (gstate->surface); + } gstate->surface = surface; /* Sometimes the user wants to return to having no target surface, * (just like after cairo_create). This can be useful for forcing * the old surface to be destroyed. */ - if (surface == NULL) + if (surface == NULL) { + gstate->surface_level = 0; return CAIRO_STATUS_SUCCESS; + } cairo_surface_reference (gstate->surface); + gstate->surface_level = surface->level; - scale = _cairo_surface_pixels_per_inch (surface) / gstate->pixels_per_inch; - _cairo_gstate_scale (gstate, scale, scale); - gstate->pixels_per_inch = _cairo_surface_pixels_per_inch (surface); + _cairo_gstate_identity_matrix (gstate); return CAIRO_STATUS_SUCCESS; } -/* XXX: Need to decide the memory mangement semantics of this - function. Should it reference the surface again? */ cairo_surface_t * -_cairo_gstate_current_target_surface (cairo_gstate_t *gstate) +_cairo_gstate_get_target (cairo_gstate_t *gstate) { if (gstate == NULL) return NULL; -/* XXX: Do we want this? - if (gstate->surface) - _cairo_surface_reference (gstate->surface); -*/ - return gstate->surface; } cairo_status_t -_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) +_cairo_gstate_set_source (cairo_gstate_t *gstate, + cairo_pattern_t *source) { - if (pattern == NULL) + if (source == NULL) return CAIRO_STATUS_NULL_POINTER; - cairo_pattern_reference (pattern); - cairo_pattern_destroy (gstate->pattern); - gstate->pattern = pattern; + cairo_pattern_reference (source); + cairo_pattern_destroy (gstate->source); + gstate->source = source; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_set_source_solid (cairo_gstate_t *gstate, + const cairo_color_t *color) +{ + cairo_status_t status; + cairo_pattern_t *source; + + source = _cairo_pattern_create_solid (color); + if (!source) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_gstate_set_source (gstate, source); + + cairo_pattern_destroy (source); return CAIRO_STATUS_SUCCESS; } cairo_pattern_t * -_cairo_gstate_current_pattern (cairo_gstate_t *gstate) +_cairo_gstate_get_source (cairo_gstate_t *gstate) { if (gstate == NULL) return NULL; -/* XXX: Do we want this? - cairo_pattern_reference (gstate->pattern); -*/ - - return gstate->pattern; + return gstate->source; } cairo_status_t @@ -433,30 +431,12 @@ _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator) } cairo_operator_t -_cairo_gstate_current_operator (cairo_gstate_t *gstate) +_cairo_gstate_get_operator (cairo_gstate_t *gstate) { return gstate->operator; } cairo_status_t -_cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, double blue) -{ - cairo_pattern_destroy (gstate->pattern); - - gstate->pattern = _cairo_pattern_create_solid (red, green, blue); - if (!gstate->pattern) - return CAIRO_STATUS_NO_MEMORY; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_current_rgb_color (cairo_gstate_t *gstate, double *red, double *green, double *blue) -{ - return _cairo_pattern_get_rgb (gstate->pattern, red, green, blue); -} - -cairo_status_t _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) { gstate->tolerance = tolerance; @@ -465,26 +445,12 @@ _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) } double -_cairo_gstate_current_tolerance (cairo_gstate_t *gstate) +_cairo_gstate_get_tolerance (cairo_gstate_t *gstate) { return gstate->tolerance; } cairo_status_t -_cairo_gstate_set_alpha (cairo_gstate_t *gstate, double alpha) -{ - gstate->alpha = alpha; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_current_alpha (cairo_gstate_t *gstate) -{ - return gstate->alpha; -} - -cairo_status_t _cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) { gstate->fill_rule = fill_rule; @@ -493,7 +459,7 @@ _cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule } cairo_fill_rule_t -_cairo_gstate_current_fill_rule (cairo_gstate_t *gstate) +_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate) { return gstate->fill_rule; } @@ -507,7 +473,7 @@ _cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) } double -_cairo_gstate_current_line_width (cairo_gstate_t *gstate) +_cairo_gstate_get_line_width (cairo_gstate_t *gstate) { return gstate->line_width; } @@ -521,7 +487,7 @@ _cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) } cairo_line_cap_t -_cairo_gstate_current_line_cap (cairo_gstate_t *gstate) +_cairo_gstate_get_line_cap (cairo_gstate_t *gstate) { return gstate->line_cap; } @@ -535,7 +501,7 @@ _cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join } cairo_line_join_t -_cairo_gstate_current_line_join (cairo_gstate_t *gstate) +_cairo_gstate_get_line_join (cairo_gstate_t *gstate) { return gstate->line_join; } @@ -572,15 +538,15 @@ _cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) } double -_cairo_gstate_current_miter_limit (cairo_gstate_t *gstate) +_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate) { return gstate->miter_limit; } void -_cairo_gstate_current_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) +_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { - cairo_matrix_copy (matrix, &gstate->ctm); + *matrix = gstate->ctm; } cairo_status_t @@ -590,10 +556,10 @@ _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) _cairo_gstate_unset_font (gstate); - _cairo_matrix_set_translate (&tmp, tx, ty); + cairo_matrix_init_translate (&tmp, tx, ty); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - _cairo_matrix_set_translate (&tmp, -tx, -ty); + cairo_matrix_init_translate (&tmp, -tx, -ty); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); return CAIRO_STATUS_SUCCESS; @@ -609,10 +575,10 @@ _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) _cairo_gstate_unset_font (gstate); - _cairo_matrix_set_scale (&tmp, sx, sy); + cairo_matrix_init_scale (&tmp, sx, sy); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - _cairo_matrix_set_scale (&tmp, 1/sx, 1/sy); + cairo_matrix_init_scale (&tmp, 1/sx, 1/sy); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); return CAIRO_STATUS_SUCCESS; @@ -625,24 +591,24 @@ _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) _cairo_gstate_unset_font (gstate); - _cairo_matrix_set_rotate (&tmp, angle); + cairo_matrix_init_rotate (&tmp, angle); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - _cairo_matrix_set_rotate (&tmp, -angle); + cairo_matrix_init_rotate (&tmp, -angle); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_concat_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) +_cairo_gstate_transform (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) { cairo_matrix_t tmp; _cairo_gstate_unset_font (gstate); - cairo_matrix_copy (&tmp, matrix); + tmp = *matrix; cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); cairo_matrix_invert (&tmp); @@ -652,16 +618,16 @@ _cairo_gstate_concat_matrix (cairo_gstate_t *gstate, } cairo_status_t -_cairo_gstate_set_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) +_cairo_gstate_set_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) { cairo_status_t status; _cairo_gstate_unset_font (gstate); - cairo_matrix_copy (&gstate->ctm, matrix); + gstate->ctm = *matrix; - cairo_matrix_copy (&gstate->ctm_inverse, matrix); + gstate->ctm_inverse = *matrix; status = cairo_matrix_invert (&gstate->ctm_inverse); if (status) return status; @@ -670,37 +636,18 @@ _cairo_gstate_set_matrix (cairo_gstate_t *gstate, } cairo_status_t -_cairo_gstate_default_matrix (cairo_gstate_t *gstate) -{ - int scale = gstate->pixels_per_inch / CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT + 0.5; - if (scale == 0) - scale = 1; - - _cairo_gstate_unset_font (gstate); - - cairo_matrix_set_identity (&gstate->font_matrix); - - cairo_matrix_set_identity (&gstate->ctm); - cairo_matrix_scale (&gstate->ctm, scale, scale); - cairo_matrix_copy (&gstate->ctm_inverse, &gstate->ctm); - cairo_matrix_invert (&gstate->ctm_inverse); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t _cairo_gstate_identity_matrix (cairo_gstate_t *gstate) { _cairo_gstate_unset_font (gstate); - cairo_matrix_set_identity (&gstate->ctm); - cairo_matrix_set_identity (&gstate->ctm_inverse); + cairo_matrix_init_identity (&gstate->ctm); + cairo_matrix_init_identity (&gstate->ctm_inverse); return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_transform_point (cairo_gstate_t *gstate, double *x, double *y) +_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->ctm, x, y); @@ -708,7 +655,8 @@ _cairo_gstate_transform_point (cairo_gstate_t *gstate, double *x, double *y) } cairo_status_t -_cairo_gstate_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy) +_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, + double *dx, double *dy) { cairo_matrix_transform_distance (&gstate->ctm, dx, dy); @@ -716,7 +664,7 @@ _cairo_gstate_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy } cairo_status_t -_cairo_gstate_inverse_transform_point (cairo_gstate_t *gstate, double *x, double *y) +_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); @@ -724,610 +672,297 @@ _cairo_gstate_inverse_transform_point (cairo_gstate_t *gstate, double *x, double } cairo_status_t -_cairo_gstate_inverse_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy) +_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, + double *dx, double *dy) { cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_gstate_new_path (cairo_gstate_t *gstate) -{ - _cairo_path_fini (&gstate->path); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y) +void +_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) { - cairo_point_t point; - - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); - - return _cairo_path_move_to (&gstate->path, &point); + cairo_matrix_transform_point (&gstate->ctm, x, y); + if (gstate->surface) { + *x += gstate->surface->device_x_offset; + *y += gstate->surface->device_y_offset; + } } -cairo_status_t -_cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y) +void +_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) { - cairo_point_t point; - - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); - - return _cairo_path_line_to (&gstate->path, &point); + if (gstate->surface) { + *x -= gstate->surface->device_x_offset; + *y -= gstate->surface->device_y_offset; + } + cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); } +/* XXX: NYI cairo_status_t -_cairo_gstate_curve_to (cairo_gstate_t *gstate, - double x0, double y0, - double x1, double y1, - double x2, double y2) -{ - cairo_point_t p0, p1, p2; - - cairo_matrix_transform_point (&gstate->ctm, &x0, &y0); - cairo_matrix_transform_point (&gstate->ctm, &x1, &y1); - cairo_matrix_transform_point (&gstate->ctm, &x2, &y2); - - p0.x = _cairo_fixed_from_double (x0); - p0.y = _cairo_fixed_from_double (y0); - - p1.x = _cairo_fixed_from_double (x1); - p1.y = _cairo_fixed_from_double (y1); - - p2.x = _cairo_fixed_from_double (x2); - p2.y = _cairo_fixed_from_double (y2); - - return _cairo_path_curve_to (&gstate->path, &p0, &p1, &p2); -} - -/* Spline deviation from the circle in radius would be given by: - - error = sqrt (x**2 + y**2) - 1 - - A simpler error function to work with is: - - e = x**2 + y**2 - 1 - - From "Good approximation of circles by curvature-continuous Bezier - curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric - Design 8 (1990) 22-41, we learn: - - abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) - - and - abs (error) =~ 1/2 * e - - Of course, this error value applies only for the particular spline - approximation that is used in _cairo_gstate_arc_segment. -*/ -static double -_arc_error_normalized (double angle) -{ - return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); -} - -static double -_arc_max_angle_for_tolerance_normalized (double tolerance) -{ - double angle, error; - int i; - - /* Use table lookup to reduce search time in most cases. */ - struct { - double angle; - double error; - } table[] = { - { M_PI / 1.0, 0.0185185185185185036127 }, - { M_PI / 2.0, 0.000272567143730179811158 }, - { M_PI / 3.0, 2.38647043651461047433e-05 }, - { M_PI / 4.0, 4.2455377443222443279e-06 }, - { M_PI / 5.0, 1.11281001494389081528e-06 }, - { M_PI / 6.0, 3.72662000942734705475e-07 }, - { M_PI / 7.0, 1.47783685574284411325e-07 }, - { M_PI / 8.0, 6.63240432022601149057e-08 }, - { M_PI / 9.0, 3.2715520137536980553e-08 }, - { M_PI / 10.0, 1.73863223499021216974e-08 }, - { M_PI / 11.0, 9.81410988043554039085e-09 }, - }; - int table_size = (sizeof (table) / sizeof (table[0])); - - for (i = 0; i < table_size; i++) - if (table[i].error < tolerance) - return table[i].angle; - - ++i; - do { - angle = M_PI / i++; - error = _arc_error_normalized (angle); - } while (error > tolerance); - - return angle; -} - -static int -_cairo_gstate_arc_segments_needed (cairo_gstate_t *gstate, - double angle, - double radius) -{ - double l1, l2, lmax; - double max_angle; - - _cairo_matrix_compute_eigen_values (&gstate->ctm, &l1, &l2); - - l1 = fabs (l1); - l2 = fabs (l2); - if (l1 > l2) - lmax = l1; - else - lmax = l2; - - max_angle = _arc_max_angle_for_tolerance_normalized (gstate->tolerance / (radius * lmax)); - - return (int) ceil (angle / max_angle); -} - -/* We want to draw a single spline approximating a circular arc radius - R from angle A to angle B. Since we want a symmetric spline that - matches the endpoints of the arc in position and slope, we know - that the spline control points must be: - - (R * cos(A), R * sin(A)) - (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) - (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) - (R * cos(B), R * sin(B)) - - for some value of h. - - "Approximation of circular arcs by cubic poynomials", Michael - Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides - various values of h along with error analysis for each. - - From that paper, a very practical value of h is: - - h = 4/3 * tan(angle/4) - - This value does not give the spline with minimal error, but it does - provide a very good approximation, (6th-order convergence), and the - error expression is quite simple, (see the comment for - _arc_error_normalized). -*/ -static cairo_status_t -_cairo_gstate_arc_segment (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle_A, double angle_B) +_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) { cairo_status_t status; - double r_sin_A, r_cos_A; - double r_sin_B, r_cos_B; - double h; - - r_sin_A = radius * sin (angle_A); - r_cos_A = radius * cos (angle_A); - r_sin_B = radius * sin (angle_B); - r_cos_B = radius * cos (angle_B); - - h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); - - status = _cairo_gstate_curve_to (gstate, - xc + r_cos_A - h * r_sin_A, yc + r_sin_A + h * r_cos_A, - xc + r_cos_B + h * r_sin_B, yc + r_sin_B - h * r_cos_B, - xc + r_cos_B, yc + r_sin_B); - if (status) - return status; + _cairo_pen_init (&gstate); return CAIRO_STATUS_SUCCESS; } +*/ -static cairo_status_t -_cairo_gstate_arc_dir (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle_min, - double angle_max, - cairo_direction_t dir) +static void +_cairo_gstate_pattern_transform (cairo_gstate_t *gstate, + cairo_pattern_t *pattern) { - cairo_status_t status; - - while (angle_max - angle_min > 4 * M_PI) - angle_max -= 2 * M_PI; - - /* Recurse if drawing arc larger than pi */ - if (angle_max - angle_min > M_PI) { - double angle_mid = angle_min + (angle_max - angle_min) / 2.0; - /* XXX: Something tells me this block could be condensed. */ - if (dir == CAIRO_DIRECTION_FORWARD) { - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min, angle_mid, dir); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_mid, angle_max, dir); - if (status) - return status; - } else { - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_mid, angle_max, dir); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min, angle_mid, dir); - if (status) - return status; - } - } else { - int i, segments; - double angle, angle_step; - - segments = _cairo_gstate_arc_segments_needed (gstate, - angle_max - angle_min, - radius); - angle_step = (angle_max - angle_min) / (double) segments; - - if (dir == CAIRO_DIRECTION_FORWARD) { - angle = angle_min; - } else { - angle = angle_max; - angle_step = - angle_step; - } - - for (i = 0; i < segments; i++, angle += angle_step) { - _cairo_gstate_arc_segment (gstate, - xc, yc, - radius, - angle, - angle + angle_step); - } - - } + cairo_matrix_t tmp_matrix = gstate->ctm_inverse; + + if (gstate->surface) + cairo_matrix_translate (&tmp_matrix, + - gstate->surface->device_x_offset, + - gstate->surface->device_y_offset); - return CAIRO_STATUS_SUCCESS; + _cairo_pattern_transform (pattern, &tmp_matrix); } cairo_status_t -_cairo_gstate_arc (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle1, double angle2) +_cairo_gstate_paint (cairo_gstate_t *gstate) { + cairo_rectangle_t rectangle; cairo_status_t status; + cairo_box_t box; + cairo_traps_t traps; - if (radius <= 0.0) - return CAIRO_STATUS_SUCCESS; - - while (angle2 < angle1) - angle2 += 2 * M_PI; - - status = _cairo_gstate_line_to (gstate, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle1, angle2, CAIRO_DIRECTION_FORWARD); - if (status) + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + + status = _cairo_surface_get_clip_extents (gstate->surface, &rectangle); + if (!CAIRO_OK (status)) return status; - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_arc_negative (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle1, double angle2) -{ - cairo_status_t status; - - if (radius <= 0.0) - return CAIRO_STATUS_SUCCESS; - - while (angle2 > angle1) - angle2 -= 2 * M_PI; - - status = _cairo_gstate_line_to (gstate, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - if (status) + box.p1.x = _cairo_fixed_from_int (rectangle.x); + box.p1.y = _cairo_fixed_from_int (rectangle.y); + box.p2.x = _cairo_fixed_from_int (rectangle.x + rectangle.width); + box.p2.y = _cairo_fixed_from_int (rectangle.y + rectangle.height); + status = _cairo_traps_init_box (&traps, &box); + if (!CAIRO_OK (status)) return status; + + _cairo_gstate_clip_and_composite_trapezoids (gstate, + gstate->source, + gstate->operator, + gstate->surface, + &traps); - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle2, angle1, CAIRO_DIRECTION_REVERSE); - if (status) - return status; + _cairo_traps_fini (&traps); return CAIRO_STATUS_SUCCESS; } -/* XXX: NYI -cairo_status_t -_cairo_gstate_arc_to (cairo_gstate_t *gstate, - double x1, double y1, - double x2, double y2, - double radius) -{ - -} -*/ - -cairo_status_t -_cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy) -{ - cairo_distance_t distance; - - cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - - distance.dx = _cairo_fixed_from_double (dx); - distance.dy = _cairo_fixed_from_double (dy); - - return _cairo_path_rel_move_to (&gstate->path, &distance); -} - -cairo_status_t -_cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy) -{ - cairo_distance_t distance; - - cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - - distance.dx = _cairo_fixed_from_double (dx); - distance.dy = _cairo_fixed_from_double (dy); - - return _cairo_path_rel_line_to (&gstate->path, &distance); -} - -cairo_status_t -_cairo_gstate_rel_curve_to (cairo_gstate_t *gstate, - double dx0, double dy0, - double dx1, double dy1, - double dx2, double dy2) -{ - cairo_distance_t distance[3]; - - cairo_matrix_transform_distance (&gstate->ctm, &dx0, &dy0); - cairo_matrix_transform_distance (&gstate->ctm, &dx1, &dy1); - cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); - - distance[0].dx = _cairo_fixed_from_double (dx0); - distance[0].dy = _cairo_fixed_from_double (dy0); - - distance[1].dx = _cairo_fixed_from_double (dx1); - distance[1].dy = _cairo_fixed_from_double (dy1); - - distance[2].dx = _cairo_fixed_from_double (dx2); - distance[2].dy = _cairo_fixed_from_double (dy2); - - return _cairo_path_rel_curve_to (&gstate->path, - &distance[0], - &distance[1], - &distance[2]); -} - -/* XXX: NYI -cairo_status_t -_cairo_gstate_stroke_path (cairo_gstate_t *gstate) +/* Combines @gstate->clip_surface using the IN operator with + * the given intermediate surface, which corresponds to the + * rectangle of the destination space given by @extents. + */ +static cairo_status_t +_cairo_gstate_combine_clip_surface (cairo_gstate_t *gstate, + cairo_surface_t *intermediate, + cairo_rectangle_t *extents) { + cairo_pattern_union_t pattern; cairo_status_t status; - _cairo_pen_init (&gstate); - return CAIRO_STATUS_SUCCESS; -} -*/ + _cairo_pattern_init_for_surface (&pattern.surface, + gstate->clip.surface); + + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + &pattern.base, + NULL, + intermediate, + extents->x - gstate->clip.rect.x, + extents->y - gstate->clip.rect.y, + 0, 0, + 0, 0, + extents->width, extents->height); + + _cairo_pattern_fini (&pattern.base); -cairo_status_t -_cairo_gstate_close_path (cairo_gstate_t *gstate) -{ - return _cairo_path_close_path (&gstate->path); + return status; } -cairo_status_t -_cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_ret) -{ - cairo_status_t status; - cairo_point_t point; - double x, y; - - status = _cairo_path_current_point (&gstate->path, &point); - if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - x = 0.0; - y = 0.0; - } else { - x = _cairo_fixed_to_double (point.x); - y = _cairo_fixed_to_double (point.y); - cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); +/* Creates a region from a cairo_rectangle_t */ +static cairo_status_t +_region_new_from_rect (cairo_rectangle_t *rect, + pixman_region16_t **region) +{ + *region = pixman_region_create (); + if (pixman_region_union_rect (*region, *region, + rect->x, rect->y, + rect->width, rect->height) != PIXMAN_REGION_STATUS_SUCCESS) { + pixman_region_destroy (*region); + return CAIRO_STATUS_NO_MEMORY; } - if (x_ret) - *x_ret = x; - if (y_ret) - *y_ret = y; - return CAIRO_STATUS_SUCCESS; } -typedef struct gstate_path_interpreter { - cairo_matrix_t ctm_inverse; - double tolerance; - cairo_point_t current_point; - - cairo_move_to_func_t *move_to; - cairo_line_to_func_t *line_to; - cairo_curve_to_func_t *curve_to; - cairo_close_path_func_t *close_path; +/* Gets the bounding box of a region as a cairo_rectangle_t */ +static void +_region_rect_extents (pixman_region16_t *region, + cairo_rectangle_t *rect) +{ + pixman_box16_t *region_extents = pixman_region_extents (region); - void *closure; -} gpi_t; + rect->x = region_extents->x1; + rect->y = region_extents->y1; + rect->width = region_extents->x2 - region_extents->x1; + rect->height = region_extents->y2 - region_extents->y1; +} +/* Intersects @region with the clipping bounds (both region + * and surface) of @gstate + */ static cairo_status_t -_gpi_move_to (void *closure, cairo_point_t *point) +_cairo_gstate_intersect_clip (cairo_gstate_t *gstate, + pixman_region16_t *region) { - gpi_t *gpi = closure; - double x, y; + if (gstate->clip.region) + pixman_region_intersect (region, gstate->clip.region, region); - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); + if (gstate->clip.surface) { + pixman_region16_t *clip_rect; + cairo_status_t status; + + status = _region_new_from_rect (&gstate->clip.rect, &clip_rect); + if (!CAIRO_OK (status)) + return status; + + if (pixman_region_intersect (region, + clip_rect, + region) != PIXMAN_REGION_STATUS_SUCCESS) + status = CAIRO_STATUS_NO_MEMORY; - cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + pixman_region_destroy (clip_rect); - gpi->move_to (gpi->closure, x, y); - gpi->current_point = *point; + if (!CAIRO_OK (status)) + return status; + } return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_gpi_line_to (void *closure, cairo_point_t *point) +_get_mask_extents (cairo_gstate_t *gstate, + cairo_pattern_t *mask, + cairo_rectangle_t *extents) { - gpi_t *gpi = closure; - double x, y; + cairo_rectangle_t clip_rect; + pixman_region16_t *clip_region; + cairo_status_t status; + + status = _cairo_surface_get_clip_extents (gstate->surface, &clip_rect); + if (!CAIRO_OK (status)) + return status; - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); + status = _region_new_from_rect (&clip_rect, &clip_region); + if (!CAIRO_OK (status)) + return status; + + status = _cairo_gstate_intersect_clip (gstate, clip_region); + if (!CAIRO_OK (status)) + return status; - cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + _region_rect_extents (clip_region, extents); - gpi->line_to (gpi->closure, x, y); - gpi->current_point = *point; + pixman_region_destroy (clip_region); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_gpi_curve_to (void *closure, - cairo_point_t *p1, - cairo_point_t *p2, - cairo_point_t *p3) +cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask) { - gpi_t *gpi = closure; + cairo_rectangle_t extents; + cairo_pattern_union_t pattern; + cairo_surface_pattern_t intermediate_pattern; + cairo_pattern_t *effective_mask; cairo_status_t status; - cairo_spline_t spline; - double x1, y1, x2, y2, x3, y3; - - if (gpi->curve_to) { - x1 = _cairo_fixed_to_double (p1->x); - y1 = _cairo_fixed_to_double (p1->y); - cairo_matrix_transform_point (&gpi->ctm_inverse, &x1, &y1); - - x2 = _cairo_fixed_to_double (p2->x); - y2 = _cairo_fixed_to_double (p2->y); - cairo_matrix_transform_point (&gpi->ctm_inverse, &x2, &y2); - - x3 = _cairo_fixed_to_double (p3->x); - y3 = _cairo_fixed_to_double (p3->y); - cairo_matrix_transform_point (&gpi->ctm_inverse, &x3, &y3); + int mask_x, mask_y; - gpi->curve_to (gpi->closure, x1, y1, x2, y2, x3, y3); - } else { - cairo_point_t *p0 = &gpi->current_point; - int i; - double x, y; + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + + _get_mask_extents (gstate, mask, &extents); + + if (gstate->clip.surface) { + /* When there is clip surface, we'll need to create a + * temporary surface that combines the clip and mask + */ + cairo_surface_t *intermediate; - status = _cairo_spline_init (&spline, p0, p1, p2, p3); - if (status == CAIRO_INT_STATUS_DEGENERATE) - return CAIRO_STATUS_SUCCESS; + intermediate = cairo_surface_create_similar (gstate->clip.surface, + CAIRO_FORMAT_A8, + extents.width, + extents.height); + if (intermediate == NULL) + return CAIRO_STATUS_NO_MEMORY; - status = _cairo_spline_decompose (&spline, gpi->tolerance); - if (status) + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + mask, NULL, intermediate, + extents.x, extents.y, + 0, 0, + 0, 0, + extents.width, extents.height); + if (!CAIRO_OK (status)) { + cairo_surface_destroy (intermediate); return status; - - for (i=1; i < spline.num_points; i++) { - x = _cairo_fixed_to_double (spline.points[i].x); - y = _cairo_fixed_to_double (spline.points[i].y); - - cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); - - gpi->line_to (gpi->closure, x, y); } - } - - gpi->current_point = *p3; - - return CAIRO_STATUS_SUCCESS; -} + + status = _cairo_gstate_combine_clip_surface (gstate, intermediate, &extents); + if (!CAIRO_OK (status)) { + cairo_surface_destroy (intermediate); + return status; + } + + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + cairo_surface_destroy (intermediate); -static cairo_status_t -_gpi_close_path (void *closure) -{ - gpi_t *gpi = closure; + effective_mask = &intermediate_pattern.base; + mask_x = extents.x; + mask_y = extents.y; + + } else { + effective_mask = mask; + mask_x = mask_y = 0; + } - gpi->close_path (gpi->closure); + _cairo_pattern_init_copy (&pattern.base, gstate->source); + _cairo_gstate_pattern_transform (gstate, &pattern.base); - gpi->current_point.x = 0; - gpi->current_point.y = 0; + status = _cairo_surface_composite (gstate->operator, + &pattern.base, + effective_mask, + gstate->surface, + extents.x, extents.y, + extents.x - mask_x, extents.y - mask_y, + extents.x, extents.y, + extents.width, extents.height); - return CAIRO_STATUS_SUCCESS; -} + if (gstate->clip.surface) + _cairo_pattern_fini (&intermediate_pattern.base); -/* It's OK for curve_path to be NULL. In that case, all curves in the - path will be decomposed into one or more calls to the line_to - function, (according to the current tolerance). */ -cairo_status_t -_cairo_gstate_interpret_path (cairo_gstate_t *gstate, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_curve_to_func_t *curve_to, - cairo_close_path_func_t *close_path, - void *closure) -{ - cairo_path_t path; - gpi_t gpi; - - /* Anything we want from gstate must be copied. We must not retain - pointers into gstate. */ - _cairo_path_init_copy (&path, &gstate->path); - - cairo_matrix_copy (&gpi.ctm_inverse, &gstate->ctm_inverse); - gpi.tolerance = gstate->tolerance; - - gpi.move_to = move_to; - gpi.line_to = line_to; - gpi.curve_to = curve_to; - gpi.close_path = close_path; - gpi.closure = closure; - - gpi.current_point.x = 0; - gpi.current_point.y = 0; - - return _cairo_path_interpret (&path, - CAIRO_DIRECTION_FORWARD, - _gpi_move_to, - _gpi_line_to, - _gpi_curve_to, - _gpi_close_path, - &gpi); -} - -/* XXX: gstate->alpha will be going away before too long, and when it - * does, it may make sense for this function to just disappear. - */ -static void -_cairo_gstate_pattern_init_copy (cairo_gstate_t *gstate, - cairo_pattern_union_t *pattern, - cairo_pattern_t *src) -{ - _cairo_pattern_init_copy (&pattern->base, src); - _cairo_pattern_transform (&pattern->base, &gstate->ctm_inverse); - _cairo_pattern_set_alpha (&pattern->base, gstate->alpha); + return status; } cairo_status_t -_cairo_gstate_stroke (cairo_gstate_t *gstate) +_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_status_t status; cairo_traps_t traps; + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + if (gstate->line_width <= 0.0) return CAIRO_STATUS_SUCCESS; @@ -1335,41 +970,40 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate) _cairo_traps_init (&traps); - status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); + status = _cairo_path_fixed_stroke_to_traps (path, gstate, &traps); if (status) { _cairo_traps_fini (&traps); return status; } _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->pattern, + gstate->source, gstate->operator, gstate->surface, &traps); _cairo_traps_fini (&traps); - _cairo_gstate_new_path (gstate); - return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_in_stroke (cairo_gstate_t *gstate, - double x, - double y, - cairo_bool_t *inside_ret) +_cairo_gstate_in_stroke (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_traps_t traps; - cairo_matrix_transform_point (&gstate->ctm, &x, &y); + _cairo_gstate_user_to_backend (gstate, &x, &y); _cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); _cairo_traps_init (&traps); - status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); + status = _cairo_path_fixed_stroke_to_traps (path, gstate, &traps); if (status) goto BAIL; @@ -1433,6 +1067,119 @@ _cairo_rectangle_empty (cairo_rectangle_t *rect) return rect->width == 0 || rect->height == 0; } +/* Given a region representing a set of trapezoids that will be + * drawn, clip the region according to the gstate and compute + * the overall extents. + */ +static cairo_status_t +_clip_and_compute_extents_region (cairo_gstate_t *gstate, + pixman_region16_t *trap_region, + cairo_rectangle_t *extents) +{ + cairo_status_t status; + + status = _cairo_gstate_intersect_clip (gstate, trap_region); + if (!CAIRO_OK (status)) + return status; + + _region_rect_extents (trap_region, extents); + + return CAIRO_STATUS_SUCCESS; +} + +/* Given a a set of trapezoids to draw, find a bounding box (non-exact) + * of the trapezoids clipped by the gstate + */ +static cairo_status_t +_clip_and_compute_extents_arbitrary (cairo_gstate_t *gstate, + cairo_traps_t *traps, + cairo_rectangle_t *extents) +{ + cairo_box_t trap_extents; + + _cairo_traps_extents (traps, &trap_extents); + _cairo_box_round_to_rectangle (&trap_extents, extents); + + if (gstate->clip.region) { + pixman_region16_t *intersection; + cairo_status_t status; + + status = _region_new_from_rect (extents, &intersection); + if (!CAIRO_OK (status)) + return status; + + if (pixman_region_intersect (intersection, + gstate->clip.region, + intersection) == PIXMAN_REGION_STATUS_SUCCESS) + _region_rect_extents (intersection, extents); + else + status = CAIRO_STATUS_NO_MEMORY; + + pixman_region_destroy (intersection); + + if (!CAIRO_OK (status)) + return status; + } + + if (gstate->clip.surface) + _cairo_rectangle_intersect (extents, &gstate->clip.rect); + + return CAIRO_STATUS_SUCCESS; +} + +/* Composites a region representing a set of trapezoids. + */ +static cairo_status_t +_composite_trap_region (cairo_gstate_t *gstate, + cairo_pattern_t *src, + cairo_operator_t operator, + cairo_surface_t *dst, + pixman_region16_t *trap_region, + cairo_rectangle_t *extents) +{ + cairo_status_t status, tmp_status; + cairo_pattern_union_t pattern; + cairo_pattern_union_t mask; + int num_rects = pixman_region_num_rects (trap_region); + + if (num_rects == 0) + return CAIRO_STATUS_SUCCESS; + + if (num_rects > 1) { + status = _cairo_surface_set_clip_region (dst, trap_region); + if (!CAIRO_OK (status)) + return status; + } + + _cairo_pattern_init_copy (&pattern.base, src); + _cairo_gstate_pattern_transform (gstate, &pattern.base); + + if (gstate->clip.surface) + _cairo_pattern_init_for_surface (&mask.surface, gstate->clip.surface); + + status = _cairo_surface_composite (gstate->operator, + &pattern.base, + gstate->clip.surface ? &mask.base : NULL, + dst, + extents->x, extents->y, + extents->x - (gstate->clip.surface ? gstate->clip.rect.x : 0), + extents->y - (gstate->clip.surface ? gstate->clip.rect.y : 0), + extents->x, extents->y, + extents->width, extents->height); + + _cairo_pattern_fini (&pattern.base); + if (gstate->clip.surface) + _cairo_pattern_fini (&mask.base); + + if (num_rects > 1) { + tmp_status = _cairo_surface_set_clip_region (dst, gstate->clip.region); + if (!CAIRO_OK (tmp_status)) + status = tmp_status; + } + + return status; +} + static void translate_traps (cairo_traps_t *traps, int x, int y) { @@ -1462,6 +1209,145 @@ translate_traps (cairo_traps_t *traps, int x, int y) } } +/* Composites a set of trapezoids in the case where we need to create + * an intermediate surface to handle gstate->clip.surface + * + * Warning: This call modifies the coordinates of traps + */ +static cairo_status_t +_composite_traps_intermediate_surface (cairo_gstate_t *gstate, + cairo_pattern_t *src, + cairo_operator_t operator, + cairo_surface_t *dst, + cairo_traps_t *traps, + cairo_rectangle_t *extents) +{ + cairo_pattern_union_t pattern; + cairo_surface_t *intermediate; + cairo_surface_pattern_t intermediate_pattern; + cairo_status_t status; + + translate_traps (traps, -extents->x, -extents->y); + + intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, + CAIRO_FORMAT_A8, + extents->width, + extents->height, + CAIRO_COLOR_TRANSPARENT); + if (intermediate == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE); + + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, + &pattern.base, + intermediate, + extents->x, extents->y, + 0, 0, + extents->width, + extents->height, + traps->traps, + traps->num_traps); + _cairo_pattern_fini (&pattern.base); + + if (!CAIRO_OK (status)) + goto out; + + status = _cairo_gstate_combine_clip_surface (gstate, intermediate, extents); + if (!CAIRO_OK (status)) + goto out; + + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + _cairo_pattern_init_copy (&pattern.base, src); + _cairo_gstate_pattern_transform (gstate, &pattern.base); + + status = _cairo_surface_composite (operator, + &pattern.base, + &intermediate_pattern.base, + dst, + extents->x, extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + _cairo_pattern_fini (&pattern.base); + _cairo_pattern_fini (&intermediate_pattern.base); + + out: + cairo_surface_destroy (intermediate); + + return status; +} + +/* Composites a region representing a set of trapezoids in the + * case of a solid source (so we can use + * _cairo_surface_fill_rectangles). + */ +static cairo_status_t +_composite_trap_region_solid (cairo_gstate_t *gstate, + cairo_solid_pattern_t *src, + cairo_operator_t operator, + cairo_surface_t *dst, + pixman_region16_t *region) +{ + int num_rects = pixman_region_num_rects (region); + pixman_box16_t *boxes = pixman_region_rects (region); + cairo_rectangle_t *rects; + cairo_status_t status; + int i; + + if (!num_rects) + return CAIRO_STATUS_SUCCESS; + + rects = malloc (sizeof (pixman_rectangle_t) * num_rects); + if (!rects) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < num_rects; i++) { + rects[i].x = boxes[i].x1; + rects[i].y = boxes[i].y1; + rects[i].width = boxes[i].x2 - boxes[i].x1; + rects[i].height = boxes[i].y2 - boxes[i].y1; + } + + status = _cairo_surface_fill_rectangles (dst, operator, + &src->color, rects, num_rects); + + free (rects); + + return status; +} + +/* Composites a set of trapezoids in the general case where + gstate->clip.surface == NULL + */ +static cairo_status_t +_composite_traps (cairo_gstate_t *gstate, + cairo_pattern_t *src, + cairo_operator_t operator, + cairo_surface_t *dst, + cairo_traps_t *traps, + cairo_rectangle_t *extents) +{ + cairo_pattern_union_t pattern; + cairo_status_t status; + + _cairo_pattern_init_copy (&pattern.base, src); + _cairo_gstate_pattern_transform (gstate, &pattern.base); + + status = _cairo_surface_composite_trapezoids (gstate->operator, + &pattern.base, dst, + extents->x, extents->y, + extents->x, extents->y, + extents->width, + extents->height, + traps->traps, + traps->num_traps); + + _cairo_pattern_fini (&pattern.base); + + return status; +} /* Warning: This call modifies the coordinates of traps */ static cairo_status_t @@ -1472,197 +1358,124 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_traps_t *traps) { cairo_status_t status; - cairo_pattern_union_t pattern; + pixman_region16_t *trap_region; cairo_rectangle_t extents; - cairo_box_t trap_extents; - + if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; if (gstate->surface == NULL) return CAIRO_STATUS_NO_TARGET_SURFACE; - _cairo_traps_extents (traps, &trap_extents); - _cairo_box_round_to_rectangle (&trap_extents, &extents); - - if (gstate->clip.surface) { - cairo_surface_t *intermediate; - cairo_surface_pattern_t intermediate_pattern; - cairo_color_t empty_color; - - _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - - if (_cairo_rectangle_empty (&extents)) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL1; - } - - translate_traps (traps, -extents.x, -extents.y); + status = _cairo_traps_extract_region (traps, &trap_region); + if (!CAIRO_OK (status)) + return status; - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, 0.); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - extents.width, - extents.height, - &empty_color); - if (intermediate == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } + if (trap_region) + status = _clip_and_compute_extents_region (gstate, trap_region, &extents); + else + status = _clip_and_compute_extents_arbitrary (gstate, traps, &extents); - _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); - - status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - &pattern.base, - intermediate, - extents.x, extents.y, - 0, 0, - extents.width, - extents.height, - traps->traps, - traps->num_traps); - _cairo_pattern_fini (&pattern.base); - - if (status) - goto BAIL2; - - - _cairo_pattern_init_for_surface (&pattern.surface, - gstate->clip.surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - intermediate, - extents.x - gstate->clip.rect.x, - extents.y - gstate->clip.rect.y, - 0, 0, - 0, 0, - extents.width, extents.height); - _cairo_pattern_fini (&pattern.base); + if (!CAIRO_OK (status)) + goto out; - if (status) - goto BAIL2; - - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - _cairo_gstate_pattern_init_copy (gstate, &pattern, src); - - status = _cairo_surface_composite (operator, - &pattern.base, - &intermediate_pattern.base, - dst, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - - _cairo_pattern_fini (&pattern.base); - _cairo_pattern_fini (&intermediate_pattern.base); - - BAIL2: - cairo_surface_destroy (intermediate); - BAIL1: + if (_cairo_rectangle_empty (&extents)) + /* Nothing to do */ + goto out; - if (status) - return status; + if (gstate->clip.surface) { + if (trap_region) { + /* If we are compositing a set of rectangles, we can set them as the + * clip region for the destination surface and use the clip surface + * as the mask. A clip region might not be supported, in which case + * we fall through to the next method + */ + status = _composite_trap_region (gstate, src, operator, dst, + trap_region, &extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto out; + } + /* Handle a clip surface by creating an intermediate surface. */ + status = _composite_traps_intermediate_surface (gstate, src, operator, + dst, traps, &extents); } else { - if (gstate->clip.region) { - pixman_box16_t box; - pixman_box16_t *intersection_extents; - pixman_region16_t *rect, *intersection; - - box.x1 = _cairo_fixed_integer_floor (trap_extents.p1.x); - box.y1 = _cairo_fixed_integer_floor (trap_extents.p1.y); - box.x2 = _cairo_fixed_integer_ceil (trap_extents.p2.x); - box.y2 = _cairo_fixed_integer_ceil (trap_extents.p2.y); - - rect = pixman_region_create_simple (&box); - if (rect == NULL) - goto bail1; - intersection = pixman_region_create(); - if (intersection == NULL) - goto bail2; - - if (pixman_region_intersect (intersection, gstate->clip.region, - rect) != PIXMAN_REGION_STATUS_SUCCESS) - goto bail3; - intersection_extents = pixman_region_extents (intersection); - - extents.x = intersection_extents->x1; - extents.y = intersection_extents->y1; - extents.width = intersection_extents->x2 - intersection_extents->x1; - extents.height = intersection_extents->y2 - intersection_extents->y1; - bail3: - pixman_region_destroy (intersection); - bail2: - pixman_region_destroy (rect); - bail1: - ; + /* No clip surface */ + if (trap_region && src->type == CAIRO_PATTERN_SOLID) { + /* Solid rectangles are handled specially */ + status = _composite_trap_region_solid (gstate, (cairo_solid_pattern_t *)src, + operator, dst, trap_region); + } else if (trap_region && pixman_region_num_rects (trap_region) <= 1) { + /* For a simple rectangle, we can just use composite(), for more + * rectangles, we'd have to set a clip region. That might still + * be a win, but it's less obvious. (Depends on the backend) + */ + status = _composite_trap_region (gstate, src, operator, dst, + trap_region, &extents); + } else { + status = _composite_traps (gstate, src, operator, + dst, traps, &extents); } - - _cairo_gstate_pattern_init_copy (gstate, &pattern, src); - - status = _cairo_surface_composite_trapezoids (gstate->operator, - &pattern.base, dst, - extents.x, extents.y, - extents.x, extents.y, - extents.width, - extents.height, - traps->traps, - traps->num_traps); - - _cairo_pattern_fini (&pattern.base); - - if (status) - return status; } + + out: + if (trap_region) + pixman_region_destroy (trap_region); - return CAIRO_STATUS_SUCCESS; + return status; } cairo_status_t -_cairo_gstate_fill (cairo_gstate_t *gstate) +_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_status_t status; cairo_traps_t traps; + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + + status = _cairo_surface_fill_path (gstate->operator, + gstate->source, + gstate->surface, + path); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + _cairo_traps_init (&traps); - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); + status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); if (status) { _cairo_traps_fini (&traps); return status; } _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->pattern, + gstate->source, gstate->operator, gstate->surface, &traps); _cairo_traps_fini (&traps); - _cairo_gstate_new_path (gstate); - return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_in_fill (cairo_gstate_t *gstate, - double x, - double y, - cairo_bool_t *inside_ret) +_cairo_gstate_in_fill (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_traps_t traps; - cairo_matrix_transform_point (&gstate->ctm, &x, &y); + _cairo_gstate_user_to_backend (gstate, &x, &y); _cairo_traps_init (&traps); - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); + status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); if (status) goto BAIL; @@ -1693,7 +1506,8 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate) } cairo_status_t -_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { @@ -1701,9 +1515,11 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, cairo_traps_t traps; cairo_box_t extents; + _cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); + _cairo_traps_init (&traps); - status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); + status = _cairo_path_fixed_stroke_to_traps (path, gstate, &traps); if (status) goto BAIL; @@ -1714,8 +1530,8 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, *x2 = _cairo_fixed_to_double (extents.p2.x); *y2 = _cairo_fixed_to_double (extents.p2.y); - cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); - cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); + _cairo_gstate_backend_to_user (gstate, x1, y1); + _cairo_gstate_backend_to_user (gstate, x2, y2); BAIL: _cairo_traps_fini (&traps); @@ -1724,7 +1540,8 @@ BAIL: } cairo_status_t -_cairo_gstate_fill_extents (cairo_gstate_t *gstate, +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { @@ -1734,7 +1551,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate, _cairo_traps_init (&traps); - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); + status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); if (status) goto BAIL; @@ -1745,8 +1562,8 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate, *x2 = _cairo_fixed_to_double (extents.p2.x); *y2 = _cairo_fixed_to_double (extents.p2.y); - cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); - cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); + _cairo_gstate_backend_to_user (gstate, x1, y1); + _cairo_gstate_backend_to_user (gstate, x2, y2); BAIL: _cairo_traps_fini (&traps); @@ -1755,8 +1572,11 @@ BAIL: } cairo_status_t -_cairo_gstate_init_clip (cairo_gstate_t *gstate) +_cairo_gstate_reset_clip (cairo_gstate_t *gstate) { + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + /* destroy any existing clip-region artifacts */ if (gstate->clip.surface) cairo_surface_destroy (gstate->clip.surface); @@ -1774,113 +1594,57 @@ _cairo_gstate_init_clip (cairo_gstate_t *gstate) return CAIRO_STATUS_SUCCESS; } -static int -extract_transformed_rectangle(cairo_matrix_t *mat, - cairo_traps_t *tr, - pixman_box16_t *box) -{ - double a, b, c, d, tx, ty; - cairo_status_t st; - - st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); - if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) - return 0; - - if (tr->num_traps == 1 - && tr->traps[0].left.p1.x == tr->traps[0].left.p2.x - && tr->traps[0].right.p1.x == tr->traps[0].right.p2.x - && tr->traps[0].left.p1.y == tr->traps[0].right.p1.y - && tr->traps[0].left.p2.y == tr->traps[0].right.p2.y - && _cairo_fixed_is_integer(tr->traps[0].left.p1.x) - && _cairo_fixed_is_integer(tr->traps[0].left.p1.y) - && _cairo_fixed_is_integer(tr->traps[0].left.p2.x) - && _cairo_fixed_is_integer(tr->traps[0].left.p2.y) - && _cairo_fixed_is_integer(tr->traps[0].right.p1.x) - && _cairo_fixed_is_integer(tr->traps[0].right.p1.y) - && _cairo_fixed_is_integer(tr->traps[0].right.p2.x) - && _cairo_fixed_is_integer(tr->traps[0].right.p2.y)) { - - box->x1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.x); - box->x2 = (short) _cairo_fixed_integer_part(tr->traps[0].right.p1.x); - box->y1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.y); - box->y2 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p2.y); - return 1; - } - return 0; -} - -/* Reset surface clip region to the one in the gstate */ -cairo_status_t -_cairo_gstate_restore_external_state (cairo_gstate_t *gstate) -{ - cairo_status_t status; - - status = CAIRO_STATUS_SUCCESS; - - if (gstate->surface) - status = _cairo_surface_set_clip_region (gstate->surface, - gstate->clip.region); - - /* If not supported we're already using surface clipping */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - status = CAIRO_STATUS_SUCCESS; - - return status; -} - cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate) +_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_status_t status; cairo_pattern_union_t pattern; cairo_traps_t traps; - cairo_color_t white_color; cairo_box_t extents; - pixman_box16_t box; + pixman_region16_t *region; + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + /* Fill the clip region as traps. */ _cairo_traps_init (&traps); - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); - if (status) { + status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps); + if (!CAIRO_OK (status)) { _cairo_traps_fini (&traps); return status; } /* Check to see if we can represent these traps as a PixRegion. */ - if (extract_transformed_rectangle (&gstate->ctm, &traps, &box)) { - - pixman_region16_t *rect = NULL; - pixman_region16_t *intersection = NULL; - + status = _cairo_traps_extract_region (&traps, ®ion); + if (!CAIRO_OK (status)) { + _cairo_traps_fini (&traps); + return status; + } + + if (region) { status = CAIRO_STATUS_SUCCESS; - rect = pixman_region_create_simple (&box); - if (rect == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - + if (gstate->clip.region == NULL) { + gstate->clip.region = region; } else { - - if (gstate->clip.region == NULL) { - gstate->clip.region = rect; - } else { - intersection = pixman_region_create(); - if (pixman_region_intersect (intersection, - gstate->clip.region, rect) - == PIXMAN_REGION_STATUS_SUCCESS) { - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = intersection; - } else { - status = CAIRO_STATUS_NO_MEMORY; - } - pixman_region_destroy (rect); + pixman_region16_t *intersection = pixman_region_create(); + + if (pixman_region_intersect (intersection, + gstate->clip.region, region) + == PIXMAN_REGION_STATUS_SUCCESS) { + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = intersection; + } else { + status = CAIRO_STATUS_NO_MEMORY; } - - if (!status) - status = _cairo_surface_set_clip_region (gstate->surface, - gstate->clip.region); + pixman_region_destroy (region); } + + if (CAIRO_OK (status)) + status = _cairo_surface_set_clip_region (gstate->surface, + gstate->clip.region); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { _cairo_traps_fini (&traps); @@ -1894,8 +1658,6 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) /* Otherwise represent the clip as a mask surface. */ - _cairo_color_init (&white_color); - if (gstate->clip.surface == NULL) { _cairo_traps_extents (&traps, &extents); _cairo_box_round_to_rectangle (&extents, &gstate->clip.rect); @@ -1904,13 +1666,13 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) CAIRO_FORMAT_A8, gstate->clip.rect.width, gstate->clip.rect.height, - &white_color); + CAIRO_COLOR_WHITE); if (gstate->clip.surface == NULL) return CAIRO_STATUS_NO_MEMORY; } translate_traps (&traps, -gstate->clip.rect.x, -gstate->clip.rect.y); - _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); + _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE); status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN, &pattern.base, @@ -1929,254 +1691,77 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_gstate_show_surface (cairo_gstate_t *gstate, - cairo_surface_t *surface, - int width, - int height) -{ - - /* We are dealing with 6 coordinate spaces in this function. this makes - * it ugly. - * - * - "Image" space is the space of the surface we're reading pixels from. - * it is the surface argument to this function. The surface has a - * matrix attached to it which maps "user" space (see below) into - * image space. - * - * - "Device" space is the space of the surface we're ultimately writing - * pixels to. It is the current surface of the gstate argument to - * this function. - * - * - "User" space is an arbitrary space defined by the user, defined - * implicitly by the gstate's CTM. The CTM maps from user space to - * device space. The CTM inverse (which is also kept at all times) - * maps from device space to user space. - * - * - "Clip" space is the space of the surface being used to clip pixels - * during compositing. Space-wise, it is a bounding box (offset+size) - * within device space. This surface is usually smaller than the device - * surface (and possibly the image surface too) and logically occupies - * a bounding box around the "clip path", situated somewhere in device - * space. The clip path is already painted on the clip surface. - * - * - "Intermediate" space is the subset of the Clip space that the - * drawing will affect, and we allocate an intermediate surface - * of this size so that we can paint in it. - * - * - "Pattern" space is another arbitrary space defined in the pattern - * element of gstate. As pixels are read from image space, they are - * combined with pixels being read from pattern space and pixels - * already existing in device space. User coordinates are converted - * to pattern space, similarly, using a matrix attached to the pattern. - * (in fact, there is a 7th space in here, which is the space of the - * surface acting as a source for the pattern) - * - * To composite these spaces, we temporarily change the image surface - * so that it can be read and written in device coordinates; in a sense - * this makes it "spatially compatible" with the clip and device spaces. - * - * - * There is also some confusion about the interaction between a clip and - * a pattern; it is assumed that in this "show surface" operation a pattern - * is to be used as an auxiliary alpha mask. this might be wrong, but it's - * what we're doing now. - * - * so, to follow the operations below, remember that in the compositing - * model, each operation is always of the form ((src IN mask) OP dst). - * that's the basic operation. - * - * so the compositing we are trying to do here, in general, involves 2 - * steps, going via a temporary surface: - * - * - combining clip and pattern pixels together into a mask channel. - * this will be ((pattern IN clip) SRC temporary). it ignores the - * pixels already in the temporary, overwriting it with the - * pattern, clipped to the clip mask. - * - * - combining temporary and "image" pixels with "device" pixels, - * with a user-provided porter/duff operator. this will be - * ((image IN temporary) OP device). - * - * if there is no clip, the degenerate case is just the second step - * with pattern standing in for temporary. - * - */ - - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_matrix_t image_to_user, image_to_device; - double device_x, device_y; - double device_width, device_height; - cairo_surface_pattern_t pattern; - cairo_box_t pattern_extents; - cairo_rectangle_t extents; - - cairo_surface_get_matrix (surface, &image_to_user); - cairo_matrix_invert (&image_to_user); - cairo_matrix_multiply (&image_to_device, &image_to_user, &gstate->ctm); - - _cairo_gstate_current_point (gstate, &device_x, &device_y); - device_width = width; - device_height = height; - _cairo_matrix_transform_bounding_box (&image_to_device, - &device_x, &device_y, - &device_width, &device_height); - - _cairo_pattern_init_for_surface (&pattern, surface); - - /* inherit surface attributes while surface attribute functions still - exist */ - pattern.base.matrix = surface->matrix; - pattern.base.filter = surface->filter; - if (surface->repeat) - pattern.base.extend = CAIRO_EXTEND_REPEAT; - else - pattern.base.extend = CAIRO_EXTEND_NONE; - - _cairo_pattern_transform (&pattern.base, &gstate->ctm_inverse); - _cairo_pattern_set_alpha (&pattern.base, gstate->alpha); - - pattern_extents.p1.x = _cairo_fixed_from_double (device_x); - pattern_extents.p1.y = _cairo_fixed_from_double (device_y); - pattern_extents.p2.x = _cairo_fixed_from_double (device_x + device_width); - pattern_extents.p2.y = _cairo_fixed_from_double (device_y + device_height); - _cairo_box_round_to_rectangle (&pattern_extents, &extents); - - if (gstate->clip.surface) - { - _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - - /* We only need to composite if the rectangle is not empty. */ - if (!_cairo_rectangle_empty (&extents)) { - cairo_surface_pattern_t clip_pattern; - - _cairo_pattern_init_for_surface (&clip_pattern, - gstate->clip.surface); - - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - &clip_pattern.base, - gstate->surface, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - - _cairo_pattern_fini (&clip_pattern.base); - } - } - else - { - /* XXX: The rendered size is sometimes 1 or 2 pixels short - * from what I expect. Need to fix this. - * KRH: I'm guessing this was due to rounding error when - * passing double coordinates for integer arguments. Using - * the extents rectangle should fix this, since it's properly - * rounded. Is this still the case? - */ - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - NULL, - gstate->surface, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - - } - - _cairo_pattern_fini (&pattern.base); - - return status; -} - static void _cairo_gstate_unset_font (cairo_gstate_t *gstate) { - if (gstate->font) { - cairo_font_destroy (gstate->font); - gstate->font = NULL; + if (gstate->scaled_font) { + cairo_scaled_font_destroy (gstate->scaled_font); + gstate->scaled_font = NULL; } } cairo_status_t -_cairo_gstate_select_font (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) +_cairo_gstate_select_font_face (cairo_gstate_t *gstate, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) { - char *new_family; + cairo_font_face_t *font_face; - new_family = strdup (family); - if (!new_family) + font_face = _cairo_simple_font_face_create (family, slant, weight); + if (!font_face) return CAIRO_STATUS_NO_MEMORY; - - _cairo_gstate_unset_font (gstate); - - gstate->font_family = new_family; - gstate->font_slant = slant; - gstate->font_weight = weight; - cairo_matrix_set_identity (&gstate->font_matrix); - + _cairo_gstate_set_font_face (gstate, font_face); + cairo_font_face_destroy (font_face); + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_scale_font (cairo_gstate_t *gstate, - double scale) +_cairo_gstate_set_font_size (cairo_gstate_t *gstate, + double size) { _cairo_gstate_unset_font (gstate); - return cairo_matrix_scale (&gstate->font_matrix, scale, scale); + cairo_matrix_init_scale (&gstate->font_matrix, size, size); + + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_transform_font (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) +_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) { - cairo_matrix_t tmp; - double a, b, c, d, tx, ty; - _cairo_gstate_unset_font (gstate); - cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); - cairo_matrix_set_affine (&tmp, a, b, c, d, 0, 0); - return cairo_matrix_multiply (&gstate->font_matrix, &gstate->font_matrix, &tmp); + gstate->font_matrix = *matrix; + + return CAIRO_STATUS_SUCCESS; } +void +_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) +{ + *matrix = gstate->font_matrix; +} cairo_status_t -_cairo_gstate_current_font (cairo_gstate_t *gstate, - cairo_font_t **font) +_cairo_gstate_get_font_face (cairo_gstate_t *gstate, + cairo_font_face_t **font_face) { cairo_status_t status; - status = _cairo_gstate_ensure_font (gstate); + status = _cairo_gstate_ensure_font_face (gstate); if (status) return status; - *font = gstate->font; + *font_face = gstate->font_face; return CAIRO_STATUS_SUCCESS; } -void -_cairo_gstate_set_font_transform (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - _cairo_gstate_unset_font (gstate); - - cairo_matrix_copy (&gstate->font_matrix, matrix); -} - -void -_cairo_gstate_current_font_transform (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - cairo_matrix_copy (matrix, &gstate->font_matrix); -} - /* * Like everything else in this file, fonts involve Too Many Coordinate Spaces; * it is easy to get confused about what's going on. @@ -2191,11 +1776,10 @@ _cairo_gstate_current_font_transform (cairo_gstate_t *gstate, * independently scale the user coordinate system *or* the font matrix, in * order to adjust the rendered size of the font. * - * The only font type exposed to the user is cairo_font_t which is a - * a font specialized to a particular scale matrix, CTM, and target - * surface. The user is responsible for not using a cairo_font_t - * after changing the parameters; doing so will produce garbled metrics. - * + * Metrics are returned in user space, whether they are obtained from + * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t + * which is aa font specialized to a particular scale matrix, CTM, and target + * surface. * * The font's view * --------------- @@ -2254,122 +1838,102 @@ _cairo_gstate_current_font_transform (cairo_gstate_t *gstate, * */ -void -_cairo_gstate_current_font_scale (cairo_gstate_t *gstate, - cairo_font_scale_t *sc) +static cairo_status_t +_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) { - cairo_matrix_t tmp; - double dummy; - cairo_matrix_multiply (&tmp, &gstate->font_matrix, &gstate->ctm); - cairo_matrix_get_affine (&tmp, - &sc->matrix[0][0], - &sc->matrix[0][1], - &sc->matrix[1][0], - &sc->matrix[1][1], - &dummy, &dummy); + if (!gstate->font_face) { + gstate->font_face = _cairo_simple_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, + CAIRO_FONT_SLANT_DEFAULT, + CAIRO_FONT_WEIGHT_DEFAULT); + if (!gstate->font_face) + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; } - + static cairo_status_t _cairo_gstate_ensure_font (cairo_gstate_t *gstate) { - cairo_font_scale_t sc; cairo_status_t status; - const char *family; - if (gstate->font) + if (gstate->scaled_font) return CAIRO_STATUS_SUCCESS; - _cairo_gstate_current_font_scale (gstate, &sc); - - if (gstate->font_family) - family = gstate->font_family; - else - family = CAIRO_FONT_FAMILY_DEFAULT; - - status = _cairo_font_create (family, - gstate->font_slant, - gstate->font_weight, - &sc, - &gstate->font); - + status = _cairo_gstate_ensure_font_face (gstate); if (status) return status; + gstate->scaled_font = cairo_scaled_font_create (gstate->font_face, + &gstate->font_matrix, + &gstate->ctm); + + if (!gstate->scaled_font) + return CAIRO_STATUS_NO_MEMORY; + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) +_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) { cairo_status_t status = _cairo_gstate_ensure_font (gstate); if (status) return status; - return cairo_font_extents (gstate->font, - &gstate->font_matrix, - extents); + return cairo_scaled_font_extents (gstate->scaled_font, extents); } cairo_status_t _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, - const unsigned char *utf8, + const char *utf8, + double x, + double y, cairo_glyph_t **glyphs, - int *nglyphs) + int *num_glyphs) { cairo_status_t status; - - cairo_point_t point; - double origin_x, origin_y; int i; status = _cairo_gstate_ensure_font (gstate); if (status) return status; - status = _cairo_path_current_point (&gstate->path, &point); - if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - origin_x = 0.0; - origin_y = 0.0; - } else { - origin_x = _cairo_fixed_to_double (point.x); - origin_y = _cairo_fixed_to_double (point.y); - cairo_matrix_transform_point (&gstate->ctm_inverse, - &origin_x, &origin_y); - } - - status = _cairo_font_text_to_glyphs (gstate->font, - utf8, glyphs, nglyphs); + status = _cairo_scaled_font_text_to_glyphs (gstate->scaled_font, + utf8, glyphs, num_glyphs); - if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs)) + if (status || !glyphs || !num_glyphs || !(*glyphs) || !(num_glyphs)) return status; /* The font responded in glyph space, starting from (0,0). Convert to user space by applying the font transform, then add any current point offset. */ - for (i = 0; i < *nglyphs; ++i) { + for (i = 0; i < *num_glyphs; ++i) { cairo_matrix_transform_point (&gstate->font_matrix, &((*glyphs)[i].x), &((*glyphs)[i].y)); - (*glyphs)[i].x += origin_x; - (*glyphs)[i].y += origin_y; + (*glyphs)[i].x += x; + (*glyphs)[i].y += y; } return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font) -{ - if (font != gstate->font) { - if (gstate->font) - cairo_font_destroy (gstate->font); - gstate->font = font; - if (gstate->font) - cairo_font_reference (gstate->font); +_cairo_gstate_set_font_face (cairo_gstate_t *gstate, + cairo_font_face_t *font_face) +{ + if (font_face != gstate->font_face) { + if (gstate->font_face) + cairo_font_face_destroy (gstate->font_face); + gstate->font_face = font_face; + if (gstate->font_face) + cairo_font_face_reference (gstate->font_face); } + + _cairo_gstate_unset_font (gstate); return CAIRO_STATUS_SUCCESS; } @@ -2386,10 +1950,9 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, if (status) return status; - cairo_font_glyph_extents (gstate->font, - &gstate->font_matrix, - glyphs, num_glyphs, - extents); + cairo_scaled_font_glyph_extents (gstate->scaled_font, + glyphs, num_glyphs, + extents); return CAIRO_STATUS_SUCCESS; } @@ -2406,6 +1969,9 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_box_t bbox; cairo_rectangle_t extents; + if (gstate->surface->level != gstate->surface_level) + return CAIRO_STATUS_BAD_NESTING; + status = _cairo_gstate_ensure_font (gstate); if (status) return status; @@ -2417,14 +1983,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, for (i = 0; i < num_glyphs; ++i) { transformed_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&gstate->ctm, - &transformed_glyphs[i].x, - &transformed_glyphs[i].y); + _cairo_gstate_user_to_backend (gstate, + &transformed_glyphs[i].x, + &transformed_glyphs[i].y); } - status = _cairo_font_glyph_bbox (gstate->font, - transformed_glyphs, num_glyphs, - &bbox); + status = _cairo_scaled_font_glyph_bbox (gstate->scaled_font, + transformed_glyphs, num_glyphs, + &bbox); _cairo_box_round_to_rectangle (&bbox, &extents); if (status) @@ -2434,7 +2000,6 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, { cairo_surface_t *intermediate; cairo_surface_pattern_t intermediate_pattern; - cairo_color_t empty_color; _cairo_rectangle_intersect (&extents, &gstate->clip.rect); @@ -2444,13 +2009,11 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, goto BAIL1; } - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, .0); intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, CAIRO_FORMAT_A8, extents.width, extents.height, - &empty_color); + CAIRO_COLOR_TRANSPARENT); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; goto BAIL1; @@ -2463,15 +2026,15 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, transformed_glyphs[i].y -= extents.y; } - _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); + _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE); - status = _cairo_font_show_glyphs (gstate->font, - CAIRO_OPERATOR_ADD, - &pattern.base, intermediate, - extents.x, extents.y, - 0, 0, - extents.width, extents.height, - transformed_glyphs, num_glyphs); + status = _cairo_scaled_font_show_glyphs (gstate->scaled_font, + CAIRO_OPERATOR_ADD, + &pattern.base, intermediate, + extents.x, extents.y, + 0, 0, + extents.width, extents.height, + transformed_glyphs, num_glyphs); _cairo_pattern_fini (&pattern.base); @@ -2497,7 +2060,8 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, goto BAIL2; _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); + _cairo_pattern_init_copy (&pattern.base, gstate->source); + _cairo_gstate_pattern_transform (gstate, &pattern.base); status = _cairo_surface_composite (gstate->operator, &pattern.base, @@ -2517,15 +2081,16 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, } else { - _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); + _cairo_pattern_init_copy (&pattern.base, gstate->source); + _cairo_gstate_pattern_transform (gstate, &pattern.base); - status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, &pattern.base, - gstate->surface, - extents.x, extents.y, - extents.x, extents.y, - extents.width, extents.height, - transformed_glyphs, num_glyphs); + status = _cairo_scaled_font_show_glyphs (gstate->scaled_font, + gstate->operator, &pattern.base, + gstate->surface, + extents.x, extents.y, + extents.x, extents.y, + extents.width, extents.height, + transformed_glyphs, num_glyphs); _cairo_pattern_fini (&pattern.base); } @@ -2537,14 +2102,19 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, } cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) { cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -2552,14 +2122,14 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, for (i = 0; i < num_glyphs; ++i) { transformed_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&gstate->ctm, - &(transformed_glyphs[i].x), - &(transformed_glyphs[i].y)); + _cairo_gstate_user_to_backend (gstate, + &(transformed_glyphs[i].x), + &(transformed_glyphs[i].y)); } - status = _cairo_font_glyph_path (gstate->font, - transformed_glyphs, num_glyphs, - &gstate->path); + status = _cairo_scaled_font_glyph_path (gstate->scaled_font, + transformed_glyphs, num_glyphs, + path); free (transformed_glyphs); return status; diff --git a/src/cairo-hash.c b/src/cairo-hash.c index d1ad5a4e2..e95894960 100644 --- a/src/cairo-hash.c +++ b/src/cairo-hash.c @@ -113,7 +113,9 @@ static const cairo_cache_arrangement_t cache_arrangements [] = { #define LIVE_ENTRY_P(cache, i) \ (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) -#ifdef CAIRO_DO_SANITY_CHECKING +#ifdef NDEBUG +#define _cache_sane_state(c) +#else static void _cache_sane_state (cairo_cache_t *cache) { @@ -125,8 +127,6 @@ _cache_sane_state (cairo_cache_t *cache) /* assert (cache->used_memory <= cache->max_memory); */ assert (cache->live_entries <= cache->arrangement->size); } -#else -#define _cache_sane_state(c) #endif static void @@ -351,8 +351,9 @@ _cairo_cache_init (cairo_cache_t *cache, #endif cache->backend = backend; - cache->entries = calloc (sizeof(cairo_cache_entry_base_t *), - cache->arrangement->size); + cache->entries = calloc (cache->arrangement->size, + sizeof(cairo_cache_entry_base_t *)); + if (cache->entries == NULL) return CAIRO_STATUS_NO_MEMORY; } diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 9745b3150..19dc7b611 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -68,7 +68,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->pixman_image = pixman_image; surface->format = format; - surface->data = (char *) pixman_image_get_data (pixman_image); + surface->data = (unsigned char *) pixman_image_get_data (pixman_image); surface->owns_data = 0; surface->width = pixman_image_get_width (pixman_image); @@ -80,8 +80,8 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, } cairo_image_surface_t * -_cairo_image_surface_create_with_masks (char *data, - cairo_format_masks_t *format, +_cairo_image_surface_create_with_masks (unsigned char *data, + cairo_format_masks_t *format, int width, int height, int stride) @@ -195,7 +195,7 @@ cairo_image_surface_create (cairo_format_t format, * be created because of lack of memory **/ cairo_surface_t * -cairo_image_surface_create_for_data (char *data, +cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, @@ -224,6 +224,38 @@ cairo_image_surface_create_for_data (char *data, return &surface->base; } +/** + * cairo_image_surface_get_width: + * @surface: a #cairo_image_surface_t + * + * Get the width of the image surface in pixels. + * + * Return value: the width of the surface in pixels. + **/ +int +cairo_image_surface_get_width (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + return image_surface->width; +} + +/** + * cairo_image_surface_get_height: + * @surface: a #cairo_image_surface_t + * + * Get the height of the image surface in pixels. + * + * Return value: the height of the surface in pixels. + **/ +int +cairo_image_surface_get_height (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + return image_surface->height; +} + static cairo_surface_t * _cairo_image_surface_create_similar (void *abstract_src, cairo_format_t format, @@ -234,20 +266,22 @@ _cairo_image_surface_create_similar (void *abstract_src, return cairo_image_surface_create (format, width, height); } -static void -_cairo_image_abstract_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_image_abstract_surface_finish (void *abstract_surface) { cairo_image_surface_t *surface = abstract_surface; - if (surface->pixman_image) + if (surface->pixman_image) { pixman_image_destroy (surface->pixman_image); + surface->pixman_image = NULL; + } if (surface->owns_data) { free (surface->data); surface->data = NULL; } - free (surface); + return CAIRO_STATUS_SUCCESS; } void @@ -256,13 +290,6 @@ _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) surface->owns_data = 1; } -static double -_cairo_image_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We'll want a way to let the user set this. */ - return 96.0; -} - static cairo_status_t _cairo_image_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, @@ -327,17 +354,17 @@ _cairo_image_surface_clone_similar (void *abstract_surface, cairo_status_t _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, - cairo_matrix_t *matrix) + const cairo_matrix_t *matrix) { pixman_transform_t pixman_transform; - pixman_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - pixman_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - pixman_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + pixman_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->xx); + pixman_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->xy); + pixman_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->x0); - pixman_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - pixman_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - pixman_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); + pixman_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->yx); + pixman_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->yy); + pixman_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->y0); pixman_transform.matrix[2][0] = 0; pixman_transform.matrix[2][1] = 0; @@ -414,32 +441,40 @@ _cairo_image_surface_set_attributes (cairo_image_surface_t *surface, return status; } +/* XXX: I think we should fix pixman to match the names/order of the + * cairo operators, but that will likely be better done at the same + * time the X server is ported to pixman, (which will change a lot of + * things in pixman I think). + */ static pixman_operator_t _pixman_operator (cairo_operator_t operator) { switch (operator) { case CAIRO_OPERATOR_CLEAR: return PIXMAN_OPERATOR_CLEAR; - case CAIRO_OPERATOR_SRC: + + case CAIRO_OPERATOR_SOURCE: return PIXMAN_OPERATOR_SRC; - case CAIRO_OPERATOR_DST: - return PIXMAN_OPERATOR_DST; case CAIRO_OPERATOR_OVER: return PIXMAN_OPERATOR_OVER; - case CAIRO_OPERATOR_OVER_REVERSE: - return PIXMAN_OPERATOR_OVER_REVERSE; case CAIRO_OPERATOR_IN: return PIXMAN_OPERATOR_IN; - case CAIRO_OPERATOR_IN_REVERSE: - return PIXMAN_OPERATOR_IN_REVERSE; case CAIRO_OPERATOR_OUT: return PIXMAN_OPERATOR_OUT; - case CAIRO_OPERATOR_OUT_REVERSE: - return PIXMAN_OPERATOR_OUT_REVERSE; case CAIRO_OPERATOR_ATOP: return PIXMAN_OPERATOR_ATOP; - case CAIRO_OPERATOR_ATOP_REVERSE: + + case CAIRO_OPERATOR_DEST: + return PIXMAN_OPERATOR_DST; + case CAIRO_OPERATOR_DEST_OVER: + return PIXMAN_OPERATOR_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return PIXMAN_OPERATOR_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return PIXMAN_OPERATOR_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: return PIXMAN_OPERATOR_ATOP_REVERSE; + case CAIRO_OPERATOR_XOR: return PIXMAN_OPERATOR_XOR; case CAIRO_OPERATOR_ADD: @@ -587,7 +622,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, * somehow. */ status = _cairo_image_surface_set_attributes (src, &attributes); if (CAIRO_OK (status)) - pixman_composite_trapezoids (operator, + pixman_composite_trapezoids (_pixman_operator (operator), src->pixman_image, dst->pixman_image, render_src_x + attributes.x_offset, @@ -600,18 +635,6 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, } static cairo_int_status_t -_cairo_image_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_image_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t _cairo_image_abstract_surface_set_clip_region (void *abstract_surface, pixman_region16_t *region) { @@ -642,6 +665,27 @@ _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_image_surface_get_extents (cairo_image_surface_t *surface, + cairo_rectangle_t *rectangle) +{ + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_image_abstract_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + cairo_image_surface_t *surface = abstract_surface; + + return _cairo_image_surface_get_extents (surface, rectangle); +} + /** * _cairo_surface_is_image: * @surface: a #cairo_surface_t @@ -658,8 +702,7 @@ _cairo_surface_is_image (cairo_surface_t *surface) static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_create_similar, - _cairo_image_abstract_surface_destroy, - _cairo_image_surface_pixels_per_inch, + _cairo_image_abstract_surface_finish, _cairo_image_surface_acquire_source_image, _cairo_image_surface_release_source_image, _cairo_image_surface_acquire_dest_image, @@ -668,8 +711,9 @@ static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_composite, _cairo_image_surface_fill_rectangles, _cairo_image_surface_composite_trapezoids, - _cairo_image_surface_copy_page, - _cairo_image_surface_show_page, + NULL, /* copy_page */ + NULL, /* show_page */ _cairo_image_abstract_surface_set_clip_region, + _cairo_image_abstract_surface_get_extents, NULL /* show_glyphs */ }; diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index 88e536e8a..82ec0dbb7 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -40,14 +40,6 @@ #include "cairoint.h" -static cairo_matrix_t const CAIRO_MATRIX_IDENTITY = { - { - {1, 0}, - {0, 1}, - {0, 0} - } -}; - static void _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); @@ -55,228 +47,185 @@ static void _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); /** - * cairo_matrix_create: - * - * Creates a new identity matrix. - * - * Return value: a newly created matrix; free with cairo_matrix_destroy(), - * or %NULL if memory couldn't be allocated. - **/ -cairo_matrix_t * -cairo_matrix_create (void) -{ - cairo_matrix_t *matrix; - - matrix = malloc (sizeof (cairo_matrix_t)); - if (matrix == NULL) - return NULL; - - _cairo_matrix_init (matrix); - - return matrix; -} - -void -_cairo_matrix_init (cairo_matrix_t *matrix) -{ - cairo_matrix_set_identity (matrix); -} - -void -_cairo_matrix_fini (cairo_matrix_t *matrix) -{ - /* nothing to do here */ -} - -/** - * cairo_matrix_destroy: - * @matrix: a #cairo_matrix_t - * - * Frees a matrix created with cairo_matrix_create. - **/ -void -cairo_matrix_destroy (cairo_matrix_t *matrix) -{ - _cairo_matrix_fini (matrix); - free (matrix); -} - -/** - * cairo_matrix_copy: - * @matrix: a #cairo_matrix_t - * @other: another #cairo_ - * - * Modifies @matrix to be identical to @other. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other) -{ - *matrix = *other; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_copy); - -/** - * cairo_matrix_set_identity: + * cairo_matrix_init_identity: * @matrix: a #cairo_matrix_t * * Modifies @matrix to be an identity transformation. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. **/ -cairo_status_t -cairo_matrix_set_identity (cairo_matrix_t *matrix) +void +cairo_matrix_init_identity (cairo_matrix_t *matrix) { - *matrix = CAIRO_MATRIX_IDENTITY; - - return CAIRO_STATUS_SUCCESS; + cairo_matrix_init (matrix, + 1, 0, + 0, 1, + 0, 0); } -slim_hidden_def(cairo_matrix_set_identity); +slim_hidden_def(cairo_matrix_init_identity); /** - * cairo_matrix_set_affine: + * cairo_matrix_init: * @matrix: a cairo_matrix_t - * @a: a component of the affine transformation - * @b: b component of the affine transformation - * @c: c component of the affine transformation - * @d: d component of the affine transformation - * @tx: X translation component of the affine transformation - * @ty: Y translation component of the affine transformation + * @xx: xx component of the affine transformation + * @yx: yx component of the affine transformation + * @xy: xy component of the affine transformation + * @yy: yy component of the affine transformation + * @x0: X translation component of the affine transformation + * @y0: Y translation component of the affine transformation * * Sets @matrix to be the affine transformation given by - * @a, b, @c, @d, @tx, @ty. The transformation is given + * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given * by: * <programlisting> - * x_new = x * a + y * c + tx; - * y_new = x * b + y * d + ty; + * x_new = xx * x + xy * y + x0; + * y_new = yx * x + yy * y + y0; * </programlisting> - * - * Return value: %CAIRO_STATUS_SUCCESS, always. **/ -cairo_status_t -cairo_matrix_set_affine (cairo_matrix_t *matrix, - double a, double b, - double c, double d, - double tx, double ty) +void +cairo_matrix_init (cairo_matrix_t *matrix, + double xx, double yx, + double xy, double yy, + double x0, double y0) { - matrix->m[0][0] = a; matrix->m[0][1] = b; - matrix->m[1][0] = c; matrix->m[1][1] = d; - matrix->m[2][0] = tx; matrix->m[2][1] = ty; - - return CAIRO_STATUS_SUCCESS; + matrix->xx = xx; matrix->yx = yx; + matrix->xy = xy; matrix->yy = yy; + matrix->x0 = x0; matrix->y0 = y0; } -slim_hidden_def(cairo_matrix_set_affine); +slim_hidden_def(cairo_matrix_init); /** - * cairo_matrix_get_affine: + * _cairo_matrix_get_affine: * @matrix: a @cairo_matrix_t - * @a: location to store a component of affine transformation, or %NULL - * @b: location to store b component of affine transformation, or %NULL - * @c: location to store c component of affine transformation, or %NULL - * @d: location to store d component of affine transformation, or %NULL - * @tx: location to store X-translation component of affine transformation, or %NULL - * @ty: location to store Y-translation component of affine transformation, or %NULL + * @xx: location to store xx component of matrix + * @yx: location to store yx component of matrix + * @xy: location to store xy component of matrix + * @yy: location to store yy component of matrix + * @x0: location to store x0 (X-translation component) of matrix, or %NULL + * @y0: location to store y0 (Y-translation component) of matrix, or %NULL * * Gets the matrix values for the affine tranformation that @matrix represents. - * See cairo_matrix_set_affine(). - * - * Return value: %CAIRO_STATUS_SUCCESS, always. + * See cairo_matrix_init(). + * + * + * This function is a leftover from the old public API, but is still + * mildly useful as an internal means for getting at the matrix + * members in a positional way. For example, when reassigning to some + * external matrix type, or when renaming members to more meaningful + * names (such as a,b,c,d,e,f) for particular manipulations. **/ -cairo_status_t -cairo_matrix_get_affine (cairo_matrix_t *matrix, - double *a, double *b, - double *c, double *d, - double *tx, double *ty) +void +_cairo_matrix_get_affine (const cairo_matrix_t *matrix, + double *xx, double *yx, + double *xy, double *yy, + double *x0, double *y0) { - if (a) - *a = matrix->m[0][0]; - if (b) - *b = matrix->m[0][1]; + *xx = matrix->xx; + *yx = matrix->yx; - if (c) - *c = matrix->m[1][0]; - if (d) - *d = matrix->m[1][1]; + *xy = matrix->xy; + *yy = matrix->yy; - if (tx) - *tx = matrix->m[2][0]; - if (ty) - *ty = matrix->m[2][1]; - - return CAIRO_STATUS_SUCCESS; + if (x0) + *x0 = matrix->x0; + if (y0) + *y0 = matrix->y0; } -cairo_status_t -_cairo_matrix_set_translate (cairo_matrix_t *matrix, +/** + * cairo_matrix_init_translate: + * @matrix: a cairo_matrix_t + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Initializes @matrix to a transformation that translates by @tx and + * @ty in the X and Y dimensions, respectively. + **/ +void +cairo_matrix_init_translate (cairo_matrix_t *matrix, double tx, double ty) { - return cairo_matrix_set_affine (matrix, - 1, 0, - 0, 1, - tx, ty); + cairo_matrix_init (matrix, + 1, 0, + 0, 1, + tx, ty); } +slim_hidden_def(cairo_matrix_init_translate); /** * cairo_matrix_translate: * @matrix: a cairo_matrix_t - * @tx: amount to rotate in the X direction - * @ty: amount to rotate in the Y direction + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction * * Applies a translation by @tx, @ty to the transformation in - * @matrix. The new transformation is given by first translating by - * @tx, @ty then applying the original transformation - * - * Return value: %CAIRO_STATUS_SUCCESS, always. + * @matrix. The effect of the new transformation is to first translate + * the coordinates by @tx and @ty, then apply the original transformation + * to the coordinates. **/ -cairo_status_t +void cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) { cairo_matrix_t tmp; - _cairo_matrix_set_translate (&tmp, tx, ty); + cairo_matrix_init_translate (&tmp, tx, ty); - return cairo_matrix_multiply (matrix, &tmp, matrix); + cairo_matrix_multiply (matrix, &tmp, matrix); } -cairo_status_t -_cairo_matrix_set_scale (cairo_matrix_t *matrix, +/** + * cairo_matrix_init_scale: + * @matrix: a cairo_matrix_t + * @sx: scale factor in the X direction + * @sy: scale factor in the Y direction + * + * Initializes @matrix to a transformation that scales by @sx and @sy + * in the X and Y dimensions, respectively. + **/ +void +cairo_matrix_init_scale (cairo_matrix_t *matrix, double sx, double sy) { - return cairo_matrix_set_affine (matrix, - sx, 0, - 0, sy, - 0, 0); + cairo_matrix_init (matrix, + sx, 0, + 0, sy, + 0, 0); } +slim_hidden_def(cairo_matrix_init_scale); /** * cairo_matrix_scale: * @matrix: a #cairo_matrix_t - * @sx: Scale factor in the X direction - * @sy: Scale factor in the Y direction + * @sx: scale factor in the X direction + * @sy: scale factor in the Y direction * - * Applies scaling by @tx, @ty to the transformation in - * @matrix. The new transformation is given by first scaling by @sx - * and @sy then applying the original transformation - * - * Return value: %CAIRO_STATUS_SUCCESS, always. + * Applies scaling by @tx, @ty to the transformation in @matrix. The + * effect of the new transformation is to first scale the coordinates + * by @sx and @sy, then apply the original transformation to the coordinates. **/ -cairo_status_t +void cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) { cairo_matrix_t tmp; - _cairo_matrix_set_scale (&tmp, sx, sy); + cairo_matrix_init_scale (&tmp, sx, sy); - return cairo_matrix_multiply (matrix, &tmp, matrix); + cairo_matrix_multiply (matrix, &tmp, matrix); } slim_hidden_def(cairo_matrix_scale); -cairo_status_t -_cairo_matrix_set_rotate (cairo_matrix_t *matrix, - double radians) +/** + * cairo_matrix_init_rotate: + * @matrix: a cairo_matrix_t + * @radians: angle of rotation, in radians. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * + * Initialized @matrix to a transformation that rotates by @radians. + **/ +void +cairo_matrix_init_rotate (cairo_matrix_t *matrix, + double radians) { double s; double c; @@ -286,35 +235,35 @@ _cairo_matrix_set_rotate (cairo_matrix_t *matrix, s = sin (radians); c = cos (radians); #endif - return cairo_matrix_set_affine (matrix, - c, s, - -s, c, - 0, 0); + cairo_matrix_init (matrix, + c, s, + -s, c, + 0, 0); } +slim_hidden_def(cairo_matrix_init_rotate); /** * cairo_matrix_rotate: * @matrix: a @cairo_matrix_t - * @radians: angle of rotation, in radians. Angles are defined - * so that an angle of 90 degrees (%M_PI radians) rotates the - * positive X axis into the positive Y axis. With the default - * Cairo choice of axis orientation, positive rotations are - * clockwise. + * @radians: angle of rotation, in radians. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. * * Applies rotation by @radians to the transformation in - * @matrix. The new transformation is given by first rotating by - * @radians then applying the original transformation - * - * Return value: %CAIRO_STATUS_SUCCESS, always. + * @matrix. The effect of the new transformation is to first rotate the + * coordinates by @radians, then apply the original transformation + * to the coordinates. **/ -cairo_status_t +void cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) { cairo_matrix_t tmp; - _cairo_matrix_set_rotate (&tmp, radians); + cairo_matrix_init_rotate (&tmp, radians); - return cairo_matrix_multiply (matrix, &tmp, matrix); + cairo_matrix_multiply (matrix, &tmp, matrix); } /** @@ -324,46 +273,47 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) * @b: a @cairo_matrix_t * * Multiplies the affine transformations in @a and @b together - * and stores the result in @result. The resulting transformation - * is given by first applying the transformation in @b then - * applying the transformation in @a. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. + * and stores the result in @result. The effect of the resulting + * transformation is to first apply the transformation in @a to the + * coordinates and then apply the transformation in @b to the + * coordinates. + * + * It is allowable for @result to be identical to either @a or @b. **/ -cairo_status_t +/* + * XXX: The ordering of the arguments to this function corresponds + * to [row_vector]*A*B. If we want to use column vectors instead, + * then we need to switch the two arguments and fix up all + * uses. + */ +void cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) { cairo_matrix_t r; - int row, col, n; - double t; - - for (row = 0; row < 3; row++) { - for (col = 0; col < 2; col++) { - if (row == 2) - t = b->m[2][col]; - else - t = 0; - for (n = 0; n < 2; n++) { - t += a->m[row][n] * b->m[n][col]; - } - r.m[row][col] = t; - } - } - *result = r; + r.xx = a->xx * b->xx + a->yx * b->xy; + r.yx = a->xx * b->yx + a->yx * b->yy; - return CAIRO_STATUS_SUCCESS; + r.xy = a->xy * b->xx + a->yy * b->xy; + r.yy = a->xy * b->yx + a->yy * b->yy; + + r.x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0; + r.y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0; + + *result = r; } slim_hidden_def(cairo_matrix_multiply); /** * cairo_matrix_transform_distance: * @matrix: a @cairo_matrix_t - * @dx: a distance in the X direction. An in/out parameter - * @dy: a distance in the Y direction. An in/out parameter + * @dx: X component of a distance vector. An in/out parameter + * @dy: Y component of a distance vector. An in/out parameter * - * Transforms the vector (@dx,@dy) by @matrix. Translation is - * ignored. In terms of the components of the affine transformation: + * Transforms the distance vector (@dx,@dy) by @matrix. This is + * similar to cairo_matrix_transform() except that the translation + * components of the transformation are ignored. The calculation of + * the returned vector is as follows: * * <programlisting> * dx2 = dx1 * a + dy1 * c; @@ -374,23 +324,17 @@ slim_hidden_def(cairo_matrix_multiply); * always transforms to the same vector. If (@x1,@y1) transforms * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. **/ -cairo_status_t -cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy) +void +cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy) { double new_x, new_y; - new_x = (matrix->m[0][0] * *dx - + matrix->m[1][0] * *dy); - new_y = (matrix->m[0][1] * *dx - + matrix->m[1][1] * *dy); + new_x = (matrix->xx * *dx + matrix->xy * *dy); + new_y = (matrix->yx * *dx + matrix->yy * *dy); *dx = new_x; *dy = new_y; - - return CAIRO_STATUS_SUCCESS; } slim_hidden_def(cairo_matrix_transform_distance); @@ -401,23 +345,19 @@ slim_hidden_def(cairo_matrix_transform_distance); * @y: Y position. An in/out parameter * * Transforms the point (@x, @y) by @matrix. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. **/ -cairo_status_t -cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y) +void +cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y) { cairo_matrix_transform_distance (matrix, x, y); - *x += matrix->m[2][0]; - *y += matrix->m[2][1]; - - return CAIRO_STATUS_SUCCESS; + *x += matrix->x0; + *y += matrix->y0; } slim_hidden_def(cairo_matrix_transform_point); -cairo_status_t -_cairo_matrix_transform_bounding_box (cairo_matrix_t *matrix, +void +_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, double *x, double *y, double *width, double *height) { @@ -466,18 +406,19 @@ _cairo_matrix_transform_bounding_box (cairo_matrix_t *matrix, *y = min_y; *width = max_x - min_x; *height = max_y - min_y; - - return CAIRO_STATUS_SUCCESS; } static void _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar) { - int row, col; + matrix->xx *= scalar; + matrix->yx *= scalar; + + matrix->xy *= scalar; + matrix->yy *= scalar; - for (row = 0; row < 3; row++) - for (col = 0; col < 2; col++) - matrix->m[row][col] *= scalar; + matrix->x0 *= scalar; + matrix->y0 *= scalar; } /* This function isn't a correct adjoint in that the implicit 1 in the @@ -490,14 +431,15 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) /* adj (A) = transpose (C:cofactor (A,i,j)) */ double a, b, c, d, tx, ty; - a = matrix->m[0][0]; b = matrix->m[0][1]; - c = matrix->m[1][0]; d = matrix->m[1][1]; - tx = matrix->m[2][0]; ty = matrix->m[2][1]; + _cairo_matrix_get_affine (matrix, + &a, &b, + &c, &d, + &tx, &ty); - cairo_matrix_set_affine (matrix, - d, -b, - -c, a, - c*ty - d*tx, b*tx - a*ty); + cairo_matrix_init (matrix, + d, -b, + -c, a, + c*ty - d*tx, b*tx - a*ty); } /** @@ -531,21 +473,21 @@ cairo_matrix_invert (cairo_matrix_t *matrix) } slim_hidden_def(cairo_matrix_invert); -cairo_status_t -_cairo_matrix_compute_determinant (cairo_matrix_t *matrix, double *det) +void +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix, + double *det) { double a, b, c, d; - a = matrix->m[0][0]; b = matrix->m[0][1]; - c = matrix->m[1][0]; d = matrix->m[1][1]; + a = matrix->xx; b = matrix->yx; + c = matrix->xy; d = matrix->yy; *det = a*d - b*c; - - return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, double *lambda2) +void +_cairo_matrix_compute_eigen_values (const cairo_matrix_t *matrix, + double *lambda1, double *lambda2) { /* The eigenvalues of an NxN matrix M are found by solving the polynomial: @@ -566,21 +508,18 @@ _cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, dou double a, b, c, d, rad; - a = matrix->m[0][0]; - b = matrix->m[0][1]; - c = matrix->m[1][0]; - d = matrix->m[1][1]; + a = matrix->xx; b = matrix->yx; + c = matrix->xy; d = matrix->yy; rad = sqrt (a*a + 2*a*d + d*d - 4*(a*d - b*c)); *lambda1 = (a + d + rad) / 2.0; *lambda2 = (a + d - rad) / 2.0; - - return CAIRO_STATUS_SUCCESS; } /* Compute the amount that each basis vector is scaled by. */ cairo_status_t -_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major) +_cairo_matrix_compute_scale_factors (const cairo_matrix_t *matrix, + double *sx, double *sy, int x_major) { double det; @@ -621,13 +560,13 @@ _cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double } cairo_bool_t -_cairo_matrix_is_integer_translation(cairo_matrix_t *mat, +_cairo_matrix_is_integer_translation(const cairo_matrix_t *mat, int *itx, int *ity) { double a, b, c, d, tx, ty; int ttx, tty; int ok = 0; - cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); + _cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); ttx = _cairo_fixed_from_double (tx); tty = _cairo_fixed_from_double (ty); ok = ((a == 1.0) diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c new file mode 100644 index 000000000..14b4486a6 --- /dev/null +++ b/src/cairo-output-stream.c @@ -0,0 +1,285 @@ +/* cairo_output_stream.c: Output stream abstraction + * + * Copyright © 2005 Red Hat, Inc + * + * 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 cairo_output_stream.c as distributed with the + * cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Author(s): + * Kristian Høgsberg <krh@redhat.com> + */ + +#include <stdio.h> +#include <locale.h> +#include <ctype.h> +#include "cairoint.h" + +struct _cairo_output_stream { + cairo_write_func_t write_data; + void *closure; + cairo_bool_t owns_closure_is_file; + unsigned long position; + cairo_status_t status; +}; + +cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_data, + void *closure) +{ + cairo_output_stream_t *stream; + + stream = malloc (sizeof (cairo_output_stream_t)); + if (stream == NULL) + return NULL; + + stream->write_data = write_data; + stream->closure = closure; + stream->owns_closure_is_file = FALSE; + stream->position = 0; + stream->status = CAIRO_STATUS_SUCCESS; + + return stream; +} + +void +_cairo_output_stream_destroy (cairo_output_stream_t *stream) +{ + if (stream->owns_closure_is_file) { + FILE *file = stream->closure; + fflush (file); + fclose (file); + } + free (stream); +} + +cairo_status_t +_cairo_output_stream_write (cairo_output_stream_t *stream, + const void *data, size_t length) +{ + if (length == 0) + return CAIRO_STATUS_SUCCESS; + + stream->status = stream->write_data (stream->closure, data, length); + stream->position += length; + + return stream->status; +} + +/* Format a double in a locale independent way and trim trailing + * zeros. Based on code from Alex Larson <alexl@redhat.com>. + * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html + */ + +static int +dtostr (char *buffer, size_t size, double d) +{ + struct lconv *locale_data; + const char *decimal_point; + int decimal_point_len; + char *p; + int decimal_len; + + snprintf (buffer, size, "%f", d); + + locale_data = localeconv (); + decimal_point = locale_data->decimal_point; + decimal_point_len = strlen (decimal_point); + + assert (decimal_point_len != 0); + p = buffer; + + if (*p == '+' || *p == '-') + p++; + + while (isdigit (*p)) + p++; + + if (strncmp (p, decimal_point, decimal_point_len) == 0) { + *p = '.'; + decimal_len = strlen (p + decimal_point_len); + memmove (p + 1, p + decimal_point_len, decimal_len); + p[1 + decimal_len] = 0; + + /* Remove trailing zeros and decimal point if possible. */ + for (p = p + decimal_len; *p == '0'; p--) + *p = 0; + + if (*p == '.') { + *p = 0; + p--; + } + } + + return p + 1 - buffer; +} + + +enum { + LENGTH_MODIFIER_LONG = 0x100 +}; + +/* Here's a limited reimplementation of printf. The reason for doing + * this is primarily to special case handling of doubles. We want + * locale independent formatting of doubles and we want to trim + * trailing zeros. This is handled by dtostr() above, and the code + * below handles everything else by calling snprintf() to do the + * formatting. This functionality is only for internal use and we + * only implement the formats we actually use. + */ + +cairo_status_t +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, va_list ap) +{ + char buffer[512]; + char *p; + const char *f; + int length_modifier; + + f = fmt; + p = buffer; + while (*f != '\0') { + if (p == buffer + sizeof (buffer)) { + _cairo_output_stream_write (stream, buffer, sizeof (buffer)); + p = buffer; + } + + if (*f != '%') { + *p++ = *f++; + continue; + } + + f++; + + _cairo_output_stream_write (stream, buffer, p - buffer); + p = buffer; + + length_modifier = 0; + if (*f == 'l') { + length_modifier = LENGTH_MODIFIER_LONG; + f++; + } + + switch (*f | length_modifier) { + case '%': + p[0] = *f; + p[1] = 0; + break; + case 'd': + snprintf (buffer, sizeof buffer, "%d", va_arg (ap, int)); + break; + case 'd' | LENGTH_MODIFIER_LONG: + snprintf (buffer, sizeof buffer, "%ld", va_arg (ap, long int)); + break; + case 'u': + snprintf (buffer, sizeof buffer, "%u", va_arg (ap, unsigned int)); + break; + case 'u' | LENGTH_MODIFIER_LONG: + snprintf (buffer, sizeof buffer, "%lu", va_arg (ap, long unsigned int)); + break; + case 'o': + snprintf (buffer, sizeof buffer, "%o", va_arg (ap, int)); + break; + case 's': + snprintf (buffer, sizeof buffer, "%s", va_arg (ap, const char *)); + break; + case 'f': + dtostr (buffer, sizeof buffer, va_arg (ap, double)); + break; + default: + ASSERT_NOT_REACHED; + } + p = buffer + strlen (buffer); + f++; + } + + _cairo_output_stream_write (stream, buffer, p - buffer); + + return stream->status; +} + +cairo_status_t +_cairo_output_stream_printf (cairo_output_stream_t *stream, + const char *fmt, ...) +{ + va_list ap; + cairo_status_t status; + + va_start (ap, fmt); + + status = _cairo_output_stream_vprintf (stream, fmt, ap); + + va_end (ap); + + return status; +} + +long +_cairo_output_stream_get_position (cairo_output_stream_t *stream) +{ + return stream->position; +} + +cairo_status_t +_cairo_output_stream_get_status (cairo_output_stream_t *stream) +{ + return stream->status; +} + + +/* Maybe this should be a configure time option, so embedded targets + * don't have to pull in stdio. */ + +static cairo_status_t +stdio_write (void *closure, const unsigned char *data, unsigned int length) +{ + FILE *fp = closure; + + if (fwrite (data, 1, length, fp) == length) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_STATUS_WRITE_ERROR; +} + +cairo_output_stream_t * +_cairo_output_stream_create_for_file (const char *filename) +{ + FILE *fp; + cairo_output_stream_t *stream; + + fp = fopen (filename, "wb"); + if (fp == NULL) + return NULL; + + stream = _cairo_output_stream_create (stdio_write, fp); + if (stream == NULL) + fclose (fp); + stream->owns_closure_is_file = TRUE; + + return stream; +} diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c index 7c5772a82..670036cfd 100644 --- a/src/cairo-path-bounds.c +++ b/src/cairo-path-bounds.c @@ -151,7 +151,9 @@ _cairo_path_bounder_close_path (void *closure) /* XXX: Perhaps this should compute a PixRegion rather than 4 doubles */ cairo_status_t -_cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2) +_cairo_path_fixed_bounds (cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) { cairo_status_t status; @@ -159,12 +161,12 @@ _cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, doub _cairo_path_bounder_init (&bounder); - status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); if (status) { *x1 = *y1 = *x2 = *y2 = 0.0; _cairo_path_bounder_fini (&bounder); diff --git a/src/cairo-path-data-private.h b/src/cairo-path-data-private.h new file mode 100644 index 000000000..e47eaaef9 --- /dev/null +++ b/src/cairo-path-data-private.h @@ -0,0 +1,55 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#ifndef CAIRO_PATH_DATA_PRIVATE_H +#define CAIRO_PATH_DATA_PRIVATE_H + +#include "cairoint.h" + +extern cairo_path_t _cairo_path_nil; + +cairo_path_t * +_cairo_path_data_create (cairo_path_fixed_t *path, + cairo_gstate_t *gstate); + +cairo_path_t * +_cairo_path_data_create_flat (cairo_path_fixed_t *path, + cairo_gstate_t *gstate); + +cairo_status_t +_cairo_path_data_append_to_context (cairo_path_t *path, + cairo_t *cr); + +#endif /* CAIRO_PATH_DATA_PRIVATE_H */ diff --git a/src/cairo-path-data.c b/src/cairo-path-data.c new file mode 100644 index 000000000..95fc3bb26 --- /dev/null +++ b/src/cairo-path-data.c @@ -0,0 +1,422 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#include "cairo-path-data-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-gstate-private.h" + +cairo_path_t +_cairo_path_nil = { NULL, 0 }; + +/* Closure for path interpretation. */ +typedef struct cairo_path_data_count { + int count; + double tolerance; + cairo_point_t current_point; +} cpdc_t; + +static cairo_status_t +_cpdc_move_to (void *closure, cairo_point_t *point) +{ + cpdc_t *cpdc = closure; + + cpdc->count += 2; + + cpdc->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdc_line_to (void *closure, cairo_point_t *point) +{ + cpdc_t *cpdc = closure; + + cpdc->count += 2; + + cpdc->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdc_curve_to (void *closure, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + cpdc_t *cpdc = closure; + + cpdc->count += 4; + + cpdc->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdc_curve_to_flatten (void *closure, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + cpdc_t *cpdc = closure; + cairo_status_t status; + cairo_spline_t spline; + int i; + + cairo_point_t *p0 = &cpdc->current_point; + + status = _cairo_spline_init (&spline, p0, p1, p2, p3); + if (status == CAIRO_INT_STATUS_DEGENERATE) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_spline_decompose (&spline, cpdc->tolerance); + if (status) + return status; + + for (i=1; i < spline.num_points; i++) + _cpdc_line_to (cpdc, &spline.points[i]); + + cpdc->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdc_close_path (void *closure) +{ + cpdc_t *cpdc = closure; + + cpdc->count += 1; + + cpdc->current_point.x = 0; + cpdc->current_point.y = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static int +_cairo_path_data_count (cairo_path_t *path, + cairo_path_fixed_t *path_fixed, + double tolerance, + cairo_bool_t flatten) +{ + cpdc_t cpdc; + + cpdc.count = 0; + cpdc.tolerance = tolerance; + cpdc.current_point.x = 0; + cpdc.current_point.y = 0; + + _cairo_path_fixed_interpret (path_fixed, + CAIRO_DIRECTION_FORWARD, + _cpdc_move_to, + _cpdc_line_to, + flatten ? + _cpdc_curve_to_flatten : + _cpdc_curve_to, + _cpdc_close_path, + &cpdc); + + return cpdc.count; +} + +/* Closure for path interpretation. */ +typedef struct cairo_path_data_populate { + cairo_path_data_t *data; + cairo_gstate_t *gstate; + cairo_point_t current_point; +} cpdp_t; + +static cairo_status_t +_cpdp_move_to (void *closure, cairo_point_t *point) +{ + cpdp_t *cpdp = closure; + cairo_path_data_t *data = cpdp->data; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + _cairo_gstate_backend_to_user (cpdp->gstate, &x, &y); + + data->header.type = CAIRO_PATH_MOVE_TO; + data->header.length = 2; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x; + data[1].point.y = y; + + cpdp->data += data->header.length; + + cpdp->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdp_line_to (void *closure, cairo_point_t *point) +{ + cpdp_t *cpdp = closure; + cairo_path_data_t *data = cpdp->data; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + _cairo_gstate_backend_to_user (cpdp->gstate, &x, &y); + + data->header.type = CAIRO_PATH_LINE_TO; + data->header.length = 2; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x; + data[1].point.y = y; + + cpdp->data += data->header.length; + + cpdp->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdp_curve_to (void *closure, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + cpdp_t *cpdp = closure; + cairo_path_data_t *data = cpdp->data; + double x1, y1; + double x2, y2; + double x3, y3; + + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + _cairo_gstate_backend_to_user (cpdp->gstate, &x1, &y1); + + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + _cairo_gstate_backend_to_user (cpdp->gstate, &x2, &y2); + + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + _cairo_gstate_backend_to_user (cpdp->gstate, &x3, &y3); + + data->header.type = CAIRO_PATH_CURVE_TO; + data->header.length = 4; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x1; + data[1].point.y = y1; + + data[2].point.x = x2; + data[2].point.y = y2; + + data[3].point.x = x3; + data[3].point.y = y3; + + cpdp->data += data->header.length; + + cpdp->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdp_curve_to_flatten (void *closure, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + cpdp_t *cpdp = closure; + cairo_status_t status; + cairo_spline_t spline; + int i; + + cairo_point_t *p0 = &cpdp->current_point; + + status = _cairo_spline_init (&spline, p0, p1, p2, p3); + if (status == CAIRO_INT_STATUS_DEGENERATE) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_spline_decompose (&spline, cpdp->gstate->tolerance); + if (status) + return status; + + for (i=1; i < spline.num_points; i++) + _cpdp_line_to (cpdp, &spline.points[i]); + + cpdp->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpdp_close_path (void *closure) +{ + cpdp_t *cpdp = closure; + cairo_path_data_t *data = cpdp->data; + + data->header.type = CAIRO_PATH_CLOSE_PATH; + data->header.length = 1; + + cpdp->data += data->header.length; + + cpdp->current_point.x = 0; + cpdp->current_point.y = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_path_data_populate (cairo_path_t *path, + cairo_path_fixed_t *path_fixed, + cairo_gstate_t *gstate, + cairo_bool_t flatten) +{ + cpdp_t cpdp; + + cpdp.data = path->data; + cpdp.gstate = gstate; + cpdp.current_point.x = 0; + cpdp.current_point.y = 0; + + _cairo_path_fixed_interpret (path_fixed, + CAIRO_DIRECTION_FORWARD, + _cpdp_move_to, + _cpdp_line_to, + flatten ? + _cpdp_curve_to_flatten : + _cpdp_curve_to, + _cpdp_close_path, + &cpdp); + + /* Sanity check the count */ + assert (cpdp.data - path->data == path->num_data); +} + +static cairo_path_t * +_cairo_path_data_create_real (cairo_path_fixed_t *path_fixed, + cairo_gstate_t *gstate, + cairo_bool_t flatten) +{ + cairo_path_t *path; + + path = malloc (sizeof (cairo_path_t)); + if (path == NULL) + return &_cairo_path_nil; + + path->num_data = _cairo_path_data_count (path, path_fixed, + gstate->tolerance, flatten); + + path->data = malloc (path->num_data * sizeof (cairo_path_data_t)); + if (path->data == NULL) { + free (path); + return &_cairo_path_nil; + } + + _cairo_path_data_populate (path, path_fixed, + gstate, flatten); + + return path; +} + +void +cairo_path_destroy (cairo_path_t *path) +{ + free (path->data); + path->num_data = 0; + free (path); +} + +cairo_path_t * +_cairo_path_data_create (cairo_path_fixed_t *path, + cairo_gstate_t *gstate) +{ + return _cairo_path_data_create_real (path, gstate, FALSE); +} + +cairo_path_t * +_cairo_path_data_create_flat (cairo_path_fixed_t *path, + cairo_gstate_t *gstate) +{ + return _cairo_path_data_create_real (path, gstate, TRUE); +} + +cairo_status_t +_cairo_path_data_append_to_context (cairo_path_t *path, + cairo_t *cr) +{ + int i; + cairo_path_data_t *p; + + for (i=0; i < path->num_data; i += path->data[i].header.length) { + p = &path->data[i]; + switch (p->header.type) { + case CAIRO_PATH_MOVE_TO: + cairo_move_to (cr, + p[1].point.x, p[1].point.y); + if (p->header.length != 2) + return CAIRO_STATUS_INVALID_PATH_DATA; + break; + case CAIRO_PATH_LINE_TO: + cairo_line_to (cr, + p[1].point.x, p[1].point.y); + if (p->header.length != 2) + return CAIRO_STATUS_INVALID_PATH_DATA; + break; + case CAIRO_PATH_CURVE_TO: + cairo_curve_to (cr, + p[1].point.x, p[1].point.y, + p[2].point.x, p[2].point.y, + p[3].point.x, p[3].point.y); + if (p->header.length != 4) + return CAIRO_STATUS_INVALID_PATH_DATA; + break; + case CAIRO_PATH_CLOSE_PATH: + cairo_close_path (cr); + if (p->header.length != 1) + return CAIRO_STATUS_INVALID_PATH_DATA; + break; + default: + return CAIRO_STATUS_INVALID_PATH_DATA; + } + } + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index dc79b6b96..c0015fc96 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-gstate-private.h" + typedef struct cairo_filler { cairo_gstate_t *gstate; cairo_traps_t *traps; @@ -171,20 +173,22 @@ _cairo_filler_close_path (void *closure) } cairo_status_t -_cairo_path_fill_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) +_cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, + cairo_gstate_t *gstate, + cairo_traps_t *traps) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t filler; _cairo_filler_init (&filler, gstate, traps); - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_filler_move_to, - _cairo_filler_line_to, - _cairo_filler_curve_to, - _cairo_filler_close_path, - &filler); + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_filler_move_to, + _cairo_filler_line_to, + _cairo_filler_curve_to, + _cairo_filler_close_path, + &filler); if (status) goto BAIL; diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h new file mode 100644 index 000000000..e8e0df194 --- /dev/null +++ b/src/cairo-path-fixed-private.h @@ -0,0 +1,74 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#ifndef CAIRO_PATH_FIXED_PRIVATE_H +#define CAIRO_PATH_FIXED_PRIVATE_H + +typedef enum cairo_path_op { + CAIRO_PATH_OP_MOVE_TO = 0, + CAIRO_PATH_OP_LINE_TO = 1, + CAIRO_PATH_OP_CURVE_TO = 2, + CAIRO_PATH_OP_CLOSE_PATH = 3 +} __attribute__ ((packed)) cairo_path_op_t; /* Don't want 32 bits if we can avoid it. */ + +#define CAIRO_PATH_BUF_SIZE 64 + +typedef struct _cairo_path_op_buf { + int num_ops; + cairo_path_op_t op[CAIRO_PATH_BUF_SIZE]; + + struct _cairo_path_op_buf *next, *prev; +} cairo_path_op_buf_t; + +typedef struct _cairo_path_arg_buf { + int num_points; + cairo_point_t points[CAIRO_PATH_BUF_SIZE]; + + struct _cairo_path_arg_buf *next, *prev; +} cairo_path_arg_buf_t; + +struct _cairo_path_fixed { + cairo_path_op_buf_t *op_buf_head; + cairo_path_op_buf_t *op_buf_tail; + + cairo_path_arg_buf_t *arg_buf_head; + cairo_path_arg_buf_t *arg_buf_tail; + + cairo_point_t last_move_point; + cairo_point_t current_point; + int has_current_point; +}; + +#endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index 08b380902..b81d862b9 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-gstate-private.h" + typedef struct cairo_stroker { cairo_gstate_t *gstate; cairo_traps_t *traps; @@ -794,7 +796,9 @@ _cairo_stroker_close_path (void *closure) } cairo_status_t -_cairo_path_stroke_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) +_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, + cairo_gstate_t *gstate, + cairo_traps_t *traps) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t stroker; @@ -802,21 +806,21 @@ _cairo_path_stroke_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_t _cairo_stroker_init (&stroker, gstate, traps); if (gstate->dash) - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - _cairo_stroker_line_to_dashed, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to_dashed, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); else - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - _cairo_stroker_line_to, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); if (status) goto BAIL; diff --git a/src/cairo-path.c b/src/cairo-path.c index 8314f601c..0940c4d1e 100644 --- a/src/cairo-path.c +++ b/src/cairo-path.c @@ -1,7 +1,8 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California - * + * Copyright © 2005 Red Hat, Inc. + * * 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 @@ -37,48 +38,52 @@ #include <stdlib.h> #include "cairoint.h" +#include "cairo-path-fixed-private.h" + /* private functions */ static cairo_status_t -_cairo_path_add (cairo_path_t *path, cairo_path_op_t op, cairo_point_t *points, int num_pts); +_cairo_path_fixed_add (cairo_path_fixed_t *path, + cairo_path_op_t op, + cairo_point_t *points, + int num_points); static void -_cairo_path_add_op_buf (cairo_path_t *path, cairo_path_op_buf_t *op); - -static cairo_status_t -_cairo_path_new_op_buf (cairo_path_t *path); +_cairo_path_fixed_add_op_buf (cairo_path_fixed_t *path, + cairo_path_op_buf_t *op_buf); static void -_cairo_path_add_arg_buf (cairo_path_t *path, cairo_path_arg_buf_t *arg); - -static cairo_status_t -_cairo_path_new_arg_buf (cairo_path_t *path); +_cairo_path_fixed_add_arg_buf (cairo_path_fixed_t *path, + cairo_path_arg_buf_t *arg_buf); static cairo_path_op_buf_t * _cairo_path_op_buf_create (void); static void -_cairo_path_op_buf_destroy (cairo_path_op_buf_t *buf); +_cairo_path_op_buf_destroy (cairo_path_op_buf_t *op_buf); static void -_cairo_path_op_buf_add (cairo_path_op_buf_t *op_buf, cairo_path_op_t op); +_cairo_path_op_buf_add_op (cairo_path_op_buf_t *op_buf, + cairo_path_op_t op); static cairo_path_arg_buf_t * _cairo_path_arg_buf_create (void); static void -_cairo_path_arg_buf_destroy (cairo_path_arg_buf_t *buf); +_cairo_path_arg_buf_destroy (cairo_path_arg_buf_t *arg_buf); static void -_cairo_path_arg_buf_add (cairo_path_arg_buf_t *arg, cairo_point_t *points, int num_points); +_cairo_path_arg_buf_add_points (cairo_path_arg_buf_t *arg_buf, + cairo_point_t *points, + int num_points); void -_cairo_path_init (cairo_path_t *path) +_cairo_path_fixed_init (cairo_path_fixed_t *path) { - path->op_head = NULL; - path->op_tail = NULL; + path->op_buf_head = NULL; + path->op_buf_tail = NULL; - path->arg_head = NULL; - path->arg_tail = NULL; + path->arg_buf_head = NULL; + path->arg_buf_tail = NULL; path->current_point.x = 0; path->current_point.y = 0; @@ -87,72 +92,85 @@ _cairo_path_init (cairo_path_t *path) } cairo_status_t -_cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) +_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, + cairo_path_fixed_t *other) { - cairo_path_op_buf_t *op, *other_op; - cairo_path_arg_buf_t *arg, *other_arg; + cairo_path_op_buf_t *op_buf, *other_op_buf; + cairo_path_arg_buf_t *arg_buf, *other_arg_buf; - _cairo_path_init (path); + _cairo_path_fixed_init (path); path->current_point = other->current_point; path->has_current_point = other->has_current_point; path->last_move_point = other->last_move_point; - for (other_op = other->op_head; other_op; other_op = other_op->next) { - op = _cairo_path_op_buf_create (); - if (op == NULL) { - _cairo_path_fini(path); + for (other_op_buf = other->op_buf_head; + other_op_buf; + other_op_buf = other_op_buf->next) + { + op_buf = _cairo_path_op_buf_create (); + if (op_buf == NULL) { + _cairo_path_fixed_fini (path); return CAIRO_STATUS_NO_MEMORY; } - *op = *other_op; - _cairo_path_add_op_buf (path, op); + memcpy (op_buf, other_op_buf, sizeof (cairo_path_op_buf_t)); + _cairo_path_fixed_add_op_buf (path, op_buf); } - for (other_arg = other->arg_head; other_arg; other_arg = other_arg->next) { - arg = _cairo_path_arg_buf_create (); - if (arg == NULL) { - _cairo_path_fini(path); + for (other_arg_buf = other->arg_buf_head; + other_arg_buf; + other_arg_buf = other_arg_buf->next) + { + arg_buf = _cairo_path_arg_buf_create (); + if (arg_buf == NULL) { + _cairo_path_fixed_fini (path); return CAIRO_STATUS_NO_MEMORY; } - *arg = *other_arg; - _cairo_path_add_arg_buf (path, arg); + memcpy (arg_buf, other_arg_buf, sizeof (cairo_path_arg_buf_t)); + _cairo_path_fixed_add_arg_buf (path, arg_buf); } return CAIRO_STATUS_SUCCESS; } void -_cairo_path_fini (cairo_path_t *path) +_cairo_path_fixed_fini (cairo_path_fixed_t *path) { - cairo_path_op_buf_t *op; - cairo_path_arg_buf_t *arg; + cairo_path_op_buf_t *op_buf; + cairo_path_arg_buf_t *arg_buf; - while (path->op_head) { - op = path->op_head; - path->op_head = op->next; - _cairo_path_op_buf_destroy (op); + while (path->op_buf_head) { + op_buf = path->op_buf_head; + path->op_buf_head = op_buf->next; + _cairo_path_op_buf_destroy (op_buf); } - path->op_tail = NULL; + path->op_buf_tail = NULL; - while (path->arg_head) { - arg = path->arg_head; - path->arg_head = arg->next; - _cairo_path_arg_buf_destroy (arg); + while (path->arg_buf_head) { + arg_buf = path->arg_buf_head; + path->arg_buf_head = arg_buf->next; + _cairo_path_arg_buf_destroy (arg_buf); } - path->arg_tail = NULL; + path->arg_buf_tail = NULL; path->has_current_point = 0; } cairo_status_t -_cairo_path_move_to (cairo_path_t *path, cairo_point_t *point) +_cairo_path_fixed_move_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) { cairo_status_t status; + cairo_point_t point; + + point.x = x; + point.y = y; - status = _cairo_path_add (path, CAIRO_PATH_OP_MOVE_TO, point, 1); + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); if (status) return status; - path->current_point = *point; + path->current_point = point; path->has_current_point = 1; path->last_move_point = path->current_point; @@ -160,91 +178,115 @@ _cairo_path_move_to (cairo_path_t *path, cairo_point_t *point) } cairo_status_t -_cairo_path_rel_move_to (cairo_path_t *path, cairo_distance_t *distance) +_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy) { - cairo_point_t point; + cairo_fixed_t x, y; - point.x = path->current_point.x + distance->dx; - point.y = path->current_point.y + distance->dy; + if (!path->has_current_point) + return CAIRO_STATUS_NO_CURRENT_POINT; + + x = path->current_point.x + dx; + y = path->current_point.y + dy; - return _cairo_path_move_to (path, &point); + return _cairo_path_fixed_move_to (path, x, y); } cairo_status_t -_cairo_path_line_to (cairo_path_t *path, cairo_point_t *point) +_cairo_path_fixed_line_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) { cairo_status_t status; + cairo_point_t point; - status = _cairo_path_add (path, CAIRO_PATH_OP_LINE_TO, point, 1); + point.x = x; + point.y = y; + + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); if (status) return status; - path->current_point = *point; + path->current_point = point; path->has_current_point = 1; return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_path_rel_line_to (cairo_path_t *path, cairo_distance_t *distance) +_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy) { - cairo_point_t point; + cairo_fixed_t x, y; + + if (!path->has_current_point) + return CAIRO_STATUS_NO_CURRENT_POINT; - point.x = path->current_point.x + distance->dx; - point.y = path->current_point.y + distance->dy; + x = path->current_point.x + dx; + y = path->current_point.y + dy; - return _cairo_path_line_to (path, &point); + return _cairo_path_fixed_line_to (path, x, y); } cairo_status_t -_cairo_path_curve_to (cairo_path_t *path, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2) +_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t x0, cairo_fixed_t y0, + cairo_fixed_t x1, cairo_fixed_t y1, + cairo_fixed_t x2, cairo_fixed_t y2) { cairo_status_t status; cairo_point_t point[3]; - point[0] = *p0; - point[1] = *p1; - point[2] = *p2; + point[0].x = x0; point[0].y = y0; + point[1].x = x1; point[1].y = y1; + point[2].x = x2; point[2].y = y2; - status = _cairo_path_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); if (status) return status; - path->current_point = *p2; + path->current_point = point[2]; path->has_current_point = 1; return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_path_rel_curve_to (cairo_path_t *path, - cairo_distance_t *d0, - cairo_distance_t *d1, - cairo_distance_t *d2) +_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t dx0, cairo_fixed_t dy0, + cairo_fixed_t dx1, cairo_fixed_t dy1, + cairo_fixed_t dx2, cairo_fixed_t dy2) { - cairo_point_t p0, p1, p2; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + + if (!path->has_current_point) + return CAIRO_STATUS_NO_CURRENT_POINT; - p0.x = path->current_point.x + d0->dx; - p0.y = path->current_point.y + d0->dy; + x0 = path->current_point.x + dx0; + y0 = path->current_point.y + dy0; - p1.x = path->current_point.x + d1->dx; - p1.y = path->current_point.y + d1->dy; + x1 = path->current_point.x + dx1; + y1 = path->current_point.y + dy1; - p2.x = path->current_point.x + d2->dx; - p2.y = path->current_point.y + d2->dy; + x2 = path->current_point.x + dx2; + y2 = path->current_point.y + dy2; - return _cairo_path_curve_to (path, &p0, &p1, &p2); + return _cairo_path_fixed_curve_to (path, + x0, y0, + x1, y1, + x2, y2); } cairo_status_t -_cairo_path_close_path (cairo_path_t *path) +_cairo_path_fixed_close_path (cairo_path_fixed_t *path) { cairo_status_t status; - status = _cairo_path_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); if (status) return status; @@ -256,120 +298,113 @@ _cairo_path_close_path (cairo_path_t *path) } cairo_status_t -_cairo_path_current_point (cairo_path_t *path, cairo_point_t *point) +_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, + cairo_fixed_t *x, + cairo_fixed_t *y) { if (! path->has_current_point) return CAIRO_STATUS_NO_CURRENT_POINT; - *point = path->current_point; + *x = path->current_point.x; + *y = path->current_point.y; return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_add (cairo_path_t *path, cairo_path_op_t op, cairo_point_t *points, int num_points) +_cairo_path_fixed_add (cairo_path_fixed_t *path, + cairo_path_op_t op, + cairo_point_t *points, + int num_points) { - cairo_status_t status; + if (path->op_buf_tail == NULL || + path->op_buf_tail->num_ops + 1 > CAIRO_PATH_BUF_SIZE) + { + cairo_path_op_buf_t *op_buf; - if (path->op_tail == NULL || path->op_tail->num_ops + 1 > CAIRO_PATH_BUF_SZ) { - status = _cairo_path_new_op_buf (path); - if (status) - return status; - } - _cairo_path_op_buf_add (path->op_tail, op); + op_buf = _cairo_path_op_buf_create (); + if (op_buf == NULL) + return CAIRO_STATUS_NO_MEMORY; - if (path->arg_tail == NULL || path->arg_tail->num_points + num_points > CAIRO_PATH_BUF_SZ) { - status = _cairo_path_new_arg_buf (path); - if (status) - return status; + _cairo_path_fixed_add_op_buf (path, op_buf); } - _cairo_path_arg_buf_add (path->arg_tail, points, num_points); - - return CAIRO_STATUS_SUCCESS; -} -static void -_cairo_path_add_op_buf (cairo_path_t *path, cairo_path_op_buf_t *op) -{ - op->next = NULL; - op->prev = path->op_tail; + _cairo_path_op_buf_add_op (path->op_buf_tail, op); - if (path->op_tail) { - path->op_tail->next = op; - } else { - path->op_head = op; - } + if (path->arg_buf_tail == NULL || + path->arg_buf_tail->num_points + num_points > CAIRO_PATH_BUF_SIZE) + { + cairo_path_arg_buf_t *arg_buf; - path->op_tail = op; -} + arg_buf = _cairo_path_arg_buf_create (); -static cairo_status_t -_cairo_path_new_op_buf (cairo_path_t *path) -{ - cairo_path_op_buf_t *op; + if (arg_buf == NULL) + return CAIRO_STATUS_NO_MEMORY; - op = _cairo_path_op_buf_create (); - if (op == NULL) - return CAIRO_STATUS_NO_MEMORY; + _cairo_path_fixed_add_arg_buf (path, arg_buf); + } - _cairo_path_add_op_buf (path, op); + _cairo_path_arg_buf_add_points (path->arg_buf_tail, points, num_points); return CAIRO_STATUS_SUCCESS; } static void -_cairo_path_add_arg_buf (cairo_path_t *path, cairo_path_arg_buf_t *arg) +_cairo_path_fixed_add_op_buf (cairo_path_fixed_t *path, + cairo_path_op_buf_t *op_buf) { - arg->next = NULL; - arg->prev = path->arg_tail; + op_buf->next = NULL; + op_buf->prev = path->op_buf_tail; - if (path->arg_tail) { - path->arg_tail->next = arg; + if (path->op_buf_tail) { + path->op_buf_tail->next = op_buf; } else { - path->arg_head = arg; + path->op_buf_head = op_buf; } - path->arg_tail = arg; + path->op_buf_tail = op_buf; } -static cairo_status_t -_cairo_path_new_arg_buf (cairo_path_t *path) +static void +_cairo_path_fixed_add_arg_buf (cairo_path_fixed_t *path, + cairo_path_arg_buf_t *arg_buf) { - cairo_path_arg_buf_t *arg; - - arg = _cairo_path_arg_buf_create (); - - if (arg == NULL) - return CAIRO_STATUS_NO_MEMORY; + arg_buf->next = NULL; + arg_buf->prev = path->arg_buf_tail; - _cairo_path_add_arg_buf (path, arg); + if (path->arg_buf_tail) { + path->arg_buf_tail->next = arg_buf; + } else { + path->arg_buf_head = arg_buf; + } - return CAIRO_STATUS_SUCCESS; + path->arg_buf_tail = arg_buf; } static cairo_path_op_buf_t * _cairo_path_op_buf_create (void) { - cairo_path_op_buf_t *op; + cairo_path_op_buf_t *op_buf; - op = malloc (sizeof (cairo_path_op_buf_t)); + op_buf = malloc (sizeof (cairo_path_op_buf_t)); - if (op) { - op->num_ops = 0; - op->next = NULL; + if (op_buf) { + op_buf->num_ops = 0; + op_buf->next = NULL; } - return op; + return op_buf; } static void -_cairo_path_op_buf_destroy (cairo_path_op_buf_t *op) +_cairo_path_op_buf_destroy (cairo_path_op_buf_t *op_buf) { - free (op); + free (op_buf); } static void -_cairo_path_op_buf_add (cairo_path_op_buf_t *op_buf, cairo_path_op_t op) +_cairo_path_op_buf_add_op (cairo_path_op_buf_t *op_buf, + cairo_path_op_t op) { op_buf->op[op_buf->num_ops++] = op; } @@ -377,31 +412,33 @@ _cairo_path_op_buf_add (cairo_path_op_buf_t *op_buf, cairo_path_op_t op) static cairo_path_arg_buf_t * _cairo_path_arg_buf_create (void) { - cairo_path_arg_buf_t *arg; + cairo_path_arg_buf_t *arg_buf; - arg = malloc (sizeof (cairo_path_arg_buf_t)); + arg_buf = malloc (sizeof (cairo_path_arg_buf_t)); - if (arg) { - arg->num_points = 0; - arg->next = NULL; + if (arg_buf) { + arg_buf->num_points = 0; + arg_buf->next = NULL; } - return arg; + return arg_buf; } static void -_cairo_path_arg_buf_destroy (cairo_path_arg_buf_t *arg) +_cairo_path_arg_buf_destroy (cairo_path_arg_buf_t *arg_buf) { - free (arg); + free (arg_buf); } static void -_cairo_path_arg_buf_add (cairo_path_arg_buf_t *arg, cairo_point_t *points, int num_points) +_cairo_path_arg_buf_add_points (cairo_path_arg_buf_t *arg_buf, + cairo_point_t *points, + int num_points) { int i; for (i=0; i < num_points; i++) { - arg->points[arg->num_points++] = points[i]; + arg_buf->points[arg_buf->num_points++] = points[i]; } } @@ -416,29 +453,30 @@ static int const num_args[] = }; cairo_status_t -_cairo_path_interpret (cairo_path_t *path, - cairo_direction_t dir, - cairo_path_move_to_func_t *move_to, - cairo_path_line_to_func_t *line_to, - cairo_path_curve_to_func_t *curve_to, - cairo_path_close_path_func_t *close_path, - void *closure) +_cairo_path_fixed_interpret (cairo_path_fixed_t *path, + cairo_direction_t dir, + cairo_path_fixed_move_to_func_t *move_to, + cairo_path_fixed_line_to_func_t *line_to, + cairo_path_fixed_curve_to_func_t *curve_to, + cairo_path_fixed_close_path_func_t *close_path, + void *closure) { cairo_status_t status; int i, arg; cairo_path_op_buf_t *op_buf; cairo_path_op_t op; - cairo_path_arg_buf_t *arg_buf = path->arg_head; + cairo_path_arg_buf_t *arg_buf = path->arg_buf_head; int buf_i = 0; cairo_point_t point[CAIRO_PATH_OP_MAX_ARGS]; - int step = (dir == CAIRO_DIRECTION_FORWARD) ? 1 : -1; + cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD); + int step = forward ? 1 : -1; - for (op_buf = (dir == CAIRO_DIRECTION_FORWARD) ? path->op_head : path->op_tail; + for (op_buf = forward ? path->op_buf_head : path->op_buf_tail; op_buf; - op_buf = (dir == CAIRO_DIRECTION_FORWARD) ? op_buf->next : op_buf->prev) + op_buf = forward ? op_buf->next : op_buf->prev) { int start, stop; - if (dir == CAIRO_DIRECTION_FORWARD) { + if (forward) { start = 0; stop = op_buf->num_ops; } else { @@ -449,7 +487,7 @@ _cairo_path_interpret (cairo_path_t *path, for (i=start; i != stop; i += step) { op = op_buf->op[i]; - if (dir == CAIRO_DIRECTION_REVERSE) { + if (! forward) { if (buf_i == 0) { arg_buf = arg_buf->prev; buf_i = arg_buf->num_points; @@ -466,7 +504,7 @@ _cairo_path_interpret (cairo_path_t *path, } } - if (dir == CAIRO_DIRECTION_REVERSE) { + if (! forward) { buf_i -= num_args[op]; } @@ -492,4 +530,3 @@ _cairo_path_interpret (cairo_path_t *path, return CAIRO_STATUS_SUCCESS; } - diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 283c36dbd..1746b6b2a 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 David Reveman + * Copyright © 2005 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without @@ -56,9 +57,8 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) pattern->ref_count = 1; pattern->extend = CAIRO_EXTEND_DEFAULT; pattern->filter = CAIRO_FILTER_DEFAULT; - pattern->alpha = 1.0; - _cairo_matrix_init (&pattern->matrix); + cairo_matrix_init_identity (&pattern->matrix); } static cairo_status_t @@ -150,15 +150,10 @@ _cairo_pattern_fini (cairo_pattern_t *pattern) void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, - double red, - double green, - double blue) + const cairo_color_t *color) { _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID); - - pattern->red = red; - pattern->green = green; - pattern->blue = blue; + pattern->color = *color; } void @@ -209,7 +204,7 @@ _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, } cairo_pattern_t * -_cairo_pattern_create_solid (double red, double green, double blue) +_cairo_pattern_create_solid (const cairo_color_t *color) { cairo_solid_pattern_t *pattern; @@ -217,7 +212,7 @@ _cairo_pattern_create_solid (double red, double green, double blue) if (pattern == NULL) return NULL; - _cairo_pattern_init_solid (pattern, red, green, blue); + _cairo_pattern_init_solid (pattern, color); return &pattern->base; } @@ -296,11 +291,8 @@ cairo_pattern_destroy (cairo_pattern_t *pattern) static cairo_status_t _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, - double offset, - double red, - double green, - double blue, - double alpha) + double offset, + cairo_color_t *color) { cairo_color_stop_t *stop; @@ -316,22 +308,48 @@ _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, stop = &pattern->stops[pattern->n_stops - 1]; stop->offset = _cairo_fixed_from_double (offset); - - _cairo_color_init (&stop->color); - _cairo_color_set_rgb (&stop->color, red, green, blue); - _cairo_color_set_alpha (&stop->color, alpha); + stop->color = *color; return CAIRO_STATUS_SUCCESS; } cairo_status_t -cairo_pattern_add_color_stop (cairo_pattern_t *pattern, - double offset, - double red, - double green, - double blue, - double alpha) +cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, + double offset, + double red, + double green, + double blue) { + cairo_color_t color; + + if (pattern->type != CAIRO_PATTERN_LINEAR && + pattern->type != CAIRO_PATTERN_RADIAL) + { + /* XXX: CAIRO_STATUS_INVALID_PATTERN? */ + return CAIRO_STATUS_SUCCESS; + } + + _cairo_restrict_value (&offset, 0.0, 1.0); + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + + _cairo_color_init_rgb (&color, red, green, blue); + return _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, + offset, + &color); +} + +cairo_status_t +cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) +{ + cairo_color_t color; + if (pattern->type != CAIRO_PATTERN_LINEAR && pattern->type != CAIRO_PATTERN_RADIAL) { @@ -345,22 +363,27 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern, _cairo_restrict_value (&blue, 0.0, 1.0); _cairo_restrict_value (&alpha, 0.0, 1.0); + _cairo_color_init_rgba (&color, red, green, blue, alpha); return _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, offset, - red, green, blue, - alpha); + &color); } cairo_status_t -cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) +cairo_pattern_set_matrix (cairo_pattern_t *pattern, + const cairo_matrix_t *matrix) { - return cairo_matrix_copy (&pattern->matrix, matrix); + pattern->matrix = *matrix; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) { - return cairo_matrix_copy (matrix, &pattern->matrix); + *matrix = pattern->matrix; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -391,35 +414,9 @@ cairo_pattern_get_extend (cairo_pattern_t *pattern) return pattern->extend; } -cairo_status_t -_cairo_pattern_get_rgb (cairo_pattern_t *pattern, - double *red, - double *green, - double *blue) -{ - - if (pattern->type == CAIRO_PATTERN_SOLID) - { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - - *red = solid->red; - *green = solid->green; - *blue = solid->blue; - } else - *red = *green = *blue = 1.0; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha) -{ - pattern->alpha = alpha; -} - void -_cairo_pattern_transform (cairo_pattern_t *pattern, - cairo_matrix_t *ctm_inverse) +_cairo_pattern_transform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm_inverse) { cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); } @@ -506,8 +503,7 @@ _cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern, op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff; op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff; op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff; - op->stops[i].color_char[3] = pattern->stops[i].color.alpha * - pattern->base.alpha * 0xff; + op->stops[i].color_char[3] = pattern->stops[i].color.alpha * 0xff; op->stops[i].offset = pattern->stops[i].offset; op->stops[i].id = i; } @@ -516,6 +512,13 @@ _cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern, qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t), _cairo_shader_color_stop_compare); + /* this scale value is used only when computing gradient values + * before the defined range, in which case stop 0 is used for both + * ends of the interpolation, making the value of 'scale' not + * actually matter, except that valgrind notices we're using + * an undefined value. + */ + op->stops[0].scale = 0; for (i = 0; i < pattern->n_stops - 1; i++) { op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset; @@ -666,8 +669,8 @@ _cairo_image_data_set_linear (cairo_linear_pattern_t *pattern, point1.x = pattern->point1.x; point1.y = pattern->point1.y; - cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); + _cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); dx = point1.x - point0.x; dy = point1.y - point0.y; @@ -721,8 +724,8 @@ _cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, point1.x = pattern->point1.x; point1.y = pattern->point1.y; - cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); + _cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); dx = point1.x - point0.x; dy = point1.y - point0.y; @@ -795,8 +798,8 @@ _cairo_image_data_set_radial (cairo_radial_pattern_t *pattern, r1_2 = c0_c1 = 0.0; /* shut up compiler */ } - cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); + _cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { @@ -889,9 +892,9 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, cairo_surface_attributes_t *attr) { cairo_image_surface_t *image; - cairo_status_t status; - uint32_t *data; - cairo_bool_t repeat = FALSE; + cairo_status_t status; + uint32_t *data; + cairo_bool_t repeat = FALSE; if (pattern->base.type == CAIRO_PATTERN_LINEAR) { cairo_bool_t is_horizontal; @@ -935,7 +938,7 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, } image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data ((char *) data, + cairo_image_surface_create_for_data ((unsigned char *) data, CAIRO_FORMAT_ARGB32, width, height, width * 4); @@ -953,10 +956,11 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, attr->x_offset = -x; attr->y_offset = -y; - cairo_matrix_set_identity (&attr->matrix); + cairo_matrix_init_identity (&attr->matrix); attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; attr->filter = CAIRO_FILTER_NEAREST; attr->acquired = FALSE; + attr->clip_saved = FALSE; return status; } @@ -971,53 +975,47 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, cairo_surface_t **out, cairo_surface_attributes_t *attribs) { - cairo_color_t color; - - _cairo_color_init (&color); - _cairo_color_set_rgb (&color, pattern->red, pattern->green, pattern->blue); - _cairo_color_set_alpha (&color, pattern->base.alpha); - *out = _cairo_surface_create_similar_solid (dst, CAIRO_FORMAT_ARGB32, 1, 1, - &color); + &pattern->color); if (*out == NULL) return CAIRO_STATUS_NO_MEMORY; attribs->x_offset = attribs->y_offset = 0; - cairo_matrix_set_identity (&attribs->matrix); + cairo_matrix_init_identity (&attribs->matrix); attribs->extend = CAIRO_EXTEND_REPEAT; attribs->filter = CAIRO_FILTER_NEAREST; attribs->acquired = FALSE; + attribs->clip_saved = FALSE; return CAIRO_STATUS_SUCCESS; } /** - * _cairo_pattern_is_opaque + * _cairo_pattern_is_opaque_solid * - * Convenience function to determine whether a pattern has an opaque - * alpha value. This is done by testing whether the pattern's alpha - * value when converted to a byte is 255, so if a backend actually - * supported deep alpha channels this function might not do the right - * thing. + * Convenience function to determine whether a pattern is an opaque + * (alpha==1.0) solid color pattern. This is done by testing whether + * the pattern's alpha value when converted to a byte is 255, so if a + * backend actually supported deep alpha channels this function might + * not do the right thing. * - * Note that for a gradient or surface pattern, the overall resulting - * alpha for the pattern can be non-opaque even this function returns - * %TRUE, since the resulting alpha is the multiplication of the - * alpha of the gradient or surface with the pattern's alpha. In - * the future, alpha will be moved from the base pattern to the - * solid pattern subtype, at which point this function should - * probably be renamed to _cairo_pattern_is_opaque_solid() - * - * Return value: %TRUE if the pattern is opaque + * Return value: %TRUE if the pattern is an opaque, solid color. **/ cairo_bool_t -_cairo_pattern_is_opaque (cairo_pattern_t *pattern) +_cairo_pattern_is_opaque_solid (cairo_pattern_t *pattern) { - return (pattern->alpha >= ((double)0xff00 / (double)0xffff)); + cairo_solid_pattern_t *solid; + + if (pattern->type != CAIRO_PATTERN_SOLID) + return FALSE; + + solid = (cairo_solid_pattern_t *) pattern; + + return (solid->color.alpha >= ((double)0xff00 / (double)0xffff)); } static cairo_int_status_t @@ -1031,84 +1029,54 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, cairo_surface_attributes_t *attr) { cairo_int_status_t status; + int tx, ty; attr->acquired = FALSE; - - /* handle pattern opacity */ - if (!_cairo_pattern_is_opaque (&pattern->base)) + attr->clip_saved = FALSE; + + if (_cairo_surface_is_image (dst)) { - cairo_surface_pattern_t tmp; - cairo_color_t color; - - _cairo_color_init (&color); - _cairo_color_set_alpha (&color, pattern->base.alpha); - - *out = _cairo_surface_create_similar_solid (dst, - CAIRO_FORMAT_ARGB32, - width, height, - &color); - if (*out == NULL) - return CAIRO_STATUS_NO_MEMORY; + cairo_image_surface_t *image; + + status = _cairo_surface_begin_reset_clip (pattern->surface); + if (!CAIRO_OK (status)) + return status; - status = _cairo_pattern_init_copy (&tmp.base, &pattern->base); - if (CAIRO_OK (status)) - { - tmp.base.alpha = 1.0; - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &tmp.base, - NULL, - *out, - x, y, 0, 0, 0, 0, - width, height); + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &attr->extra); + if (!CAIRO_OK (status)) + return status; - _cairo_pattern_fini (&tmp.base); - } + _cairo_surface_end (pattern->surface); - if (status) { - cairo_surface_destroy (*out); - return status; - } - - attr->x_offset = -x; - attr->y_offset = -y; - attr->extend = CAIRO_EXTEND_NONE; - attr->filter = CAIRO_FILTER_NEAREST; - - cairo_matrix_set_identity (&attr->matrix); + *out = &image->base; + attr->acquired = TRUE; } else { - int tx, ty; - - if (_cairo_surface_is_image (dst)) - { - cairo_image_surface_t *image; - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &attr->extra); - if (CAIRO_OK (status)) - *out = &image->base; + status = _cairo_surface_begin_reset_clip (pattern->surface); + if (!CAIRO_OK (status)) + return status; - attr->acquired = TRUE; - } - else - status = _cairo_surface_clone_similar (dst, pattern->surface, out); - - attr->extend = pattern->base.extend; - attr->filter = pattern->base.filter; - if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, - &tx, &ty)) - { - cairo_matrix_set_identity (&attr->matrix); - attr->x_offset = tx; - attr->y_offset = ty; - } - else - { - attr->matrix = pattern->base.matrix; - attr->x_offset = attr->y_offset = 0; - } + status = _cairo_surface_clone_similar (dst, pattern->surface, out); + _cairo_surface_end (pattern->surface); + } + + attr->extend = pattern->base.extend; + attr->filter = pattern->base.filter; + if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, + &tx, &ty)) + { + cairo_matrix_init_identity (&attr->matrix); + attr->x_offset = tx; + attr->y_offset = ty; + attr->filter = CAIRO_FILTER_NEAREST; + } + else + { + attr->matrix = pattern->base.matrix; + attr->x_offset = attr->y_offset = 0; } return status; @@ -1141,14 +1109,16 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, cairo_surface_t **surface_out, cairo_surface_attributes_t *attributes) { + cairo_status_t status; + switch (pattern->type) { case CAIRO_PATTERN_SOLID: { cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern; - return _cairo_pattern_acquire_surface_for_solid (src, dst, - x, y, width, height, - surface_out, - attributes); + status = _cairo_pattern_acquire_surface_for_solid (src, dst, + x, y, width, height, + surface_out, + attributes); } break; case CAIRO_PATTERN_LINEAR: case CAIRO_PATTERN_RADIAL: { @@ -1157,47 +1127,53 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, /* fast path for gradients with less than 2 color stops */ if (src->n_stops < 2) { + const cairo_color_t *color; cairo_solid_pattern_t solid; if (src->n_stops) - { - _cairo_pattern_init_solid (&solid, - src->stops->color.red, - src->stops->color.green, - src->stops->color.blue); - _cairo_pattern_set_alpha (&solid.base, - src->stops->color.alpha); - } + color = &src->stops->color; else - { - _cairo_pattern_init_solid (&solid, 0.0, 0.0, 0.0); - _cairo_pattern_set_alpha (&solid.base, 0.0); - } + color = CAIRO_COLOR_TRANSPARENT; - return _cairo_pattern_acquire_surface_for_solid (&solid, dst, - x, y, - width, height, - surface_out, - attributes); + _cairo_pattern_init_solid (&solid, color); + + status = _cairo_pattern_acquire_surface_for_solid (&solid, dst, + x, y, + width, height, + surface_out, + attributes); } else - return _cairo_pattern_acquire_surface_for_gradient (src, dst, - x, y, - width, height, - surface_out, - attributes); + status = _cairo_pattern_acquire_surface_for_gradient (src, dst, + x, y, + width, height, + surface_out, + attributes); } break; case CAIRO_PATTERN_SURFACE: { cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; - return _cairo_pattern_acquire_surface_for_surface (src, dst, - x, y, width, height, - surface_out, - attributes); + status = _cairo_pattern_acquire_surface_for_surface (src, dst, + x, y, width, height, + surface_out, + attributes); } break; + default: + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + + + if (CAIRO_OK (status) && (*surface_out)->clip_region) { + status = _cairo_surface_begin_reset_clip (*surface_out); + if (!CAIRO_OK (status)) { + _cairo_pattern_release_surface (dst, *surface_out, attributes); + return status; + } + + attributes->clip_saved = TRUE; } - return CAIRO_INT_STATUS_UNSUPPORTED; + return status; } /** @@ -1213,11 +1189,15 @@ _cairo_pattern_release_surface (cairo_surface_t *dst, cairo_surface_t *surface, cairo_surface_attributes_t *attributes) { + if (attributes->clip_saved) + _cairo_surface_end (surface); + if (attributes->acquired) + { _cairo_surface_release_source_image (dst, (cairo_image_surface_t *) surface, attributes->extra); - else + } else cairo_surface_destroy (surface); } @@ -1237,71 +1217,44 @@ _cairo_pattern_acquire_surfaces (cairo_pattern_t *src, cairo_surface_attributes_t *mask_attributes) { cairo_int_status_t status; - cairo_pattern_union_t tmp; - cairo_bool_t src_opaque, mask_opaque; - double src_alpha, mask_alpha; - src_opaque = _cairo_pattern_is_opaque (src); - mask_opaque = !mask || _cairo_pattern_is_opaque (mask); - - /* For surface patterns, we move any translucency from src->alpha - * to mask->alpha so we can use the source unchanged. Otherwise we - * move the translucency from mask->alpha to src->alpha so that - * we can drop the mask if possible. - */ - if (src->type == CAIRO_PATTERN_SURFACE) - { - if (mask) { - mask_opaque = mask_opaque && src_opaque; - mask_alpha = mask->alpha * src->alpha; - } else { - mask_opaque = src_opaque; - mask_alpha = src->alpha; - } - - src_alpha = 1.0; - src_opaque = TRUE; - } - else - { - if (mask) - { - src_opaque = mask_opaque && src_opaque; - src_alpha = mask->alpha * src->alpha; - /* FIXME: This needs changing when we support RENDER - * style 4-channel masks. - */ - if (mask->type == CAIRO_PATTERN_SOLID) - mask = NULL; - } else - src_alpha = src->alpha; - - mask_alpha = 1.0; - mask_opaque = TRUE; + /* If src and mask are both solid, then the mask alpha can be + * combined into src and mask can be ignored. */ + + /* XXX: This optimization assumes that there is no color + * information in mask, so this will need to change when we + * support RENDER-style 4-channel masks. */ + if (src->type == CAIRO_PATTERN_SOLID && + mask && mask->type == CAIRO_PATTERN_SOLID) + { + cairo_color_t combined; + cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; + cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; + + combined = src_solid->color; + _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); + + _cairo_pattern_init_solid (&tmp.solid, &combined); + + mask = NULL; + } else { + _cairo_pattern_init_copy (&tmp.base, src); } - _cairo_pattern_init_copy (&tmp.base, src); - _cairo_pattern_set_alpha (&tmp.base, src_alpha); - status = _cairo_pattern_acquire_surface (&tmp.base, dst, src_x, src_y, width, height, src_out, src_attributes); - + _cairo_pattern_fini (&tmp.base); if (status) return status; - if (mask || !mask_opaque) + if (mask) { - if (mask) - _cairo_pattern_init_copy (&tmp.base, mask); - else - _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); - - _cairo_pattern_set_alpha (&tmp.base, mask_alpha); + _cairo_pattern_init_copy (&tmp.base, mask); status = _cairo_pattern_acquire_surface (&tmp.base, dst, mask_x, mask_y, diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index fee918355..228c2c89a 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -153,13 +153,15 @@ struct cairo_pdf_stream { }; struct cairo_pdf_document { - FILE *file; + cairo_output_stream_t *output_stream; unsigned long refcount; + cairo_surface_t *owner; + cairo_bool_t finished; - double width_inches; - double height_inches; - double x_ppi; - double y_ppi; + double width; + double height; + double x_dpi; + double y_dpi; unsigned int next_available_id; unsigned int pages_id; @@ -175,8 +177,8 @@ struct cairo_pdf_document { struct cairo_pdf_surface { cairo_surface_t base; - double width_inches; - double height_inches; + double width; + double height; cairo_pdf_document_t *document; cairo_pdf_stream_t *current_stream; @@ -188,17 +190,19 @@ struct cairo_pdf_surface { cairo_array_t fonts; }; +#define DEFAULT_DPI 300 static cairo_pdf_document_t * -_cairo_pdf_document_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch); +_cairo_pdf_document_create (cairo_output_stream_t *stream, + double width, + double height); static void _cairo_pdf_document_destroy (cairo_pdf_document_t *document); +static cairo_status_t +_cairo_pdf_document_finish (cairo_pdf_document_t *document); + static void _cairo_pdf_document_reference (cairo_pdf_document_t *document); @@ -214,11 +218,12 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); static cairo_pdf_stream_t * _cairo_pdf_document_open_stream (cairo_pdf_document_t *document, - const char *extra_entries); + const char *fmt, + ...); static cairo_surface_t * _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, - double width_inches, - double height_inches); + double width, + double height); static void _cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface, cairo_pdf_stream_t *stream); @@ -319,8 +324,6 @@ cairo_pdf_ft_font_create (cairo_pdf_document_t *document, if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) goto fail1; - font->base.unscaled_font = unscaled_font; - _cairo_unscaled_font_reference (unscaled_font); font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); if (font->glyphs == NULL) goto fail2; @@ -457,7 +460,7 @@ static int cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font, unsigned long tag) { - char *buffer; + unsigned char *buffer; unsigned long size; size = 0; @@ -475,7 +478,7 @@ cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font, unsigned long start_offset, index, size; TT_Header *header; unsigned long begin, end; - char *buffer; + unsigned char *buffer; int i; union { unsigned char *bytes; @@ -814,7 +817,7 @@ _cairo_pdf_document_new_object (cairo_pdf_document_t *document) { cairo_pdf_object_t object; - object.offset = ftell (document->file); + object.offset = _cairo_output_stream_get_position (document->output_stream); if (_cairo_array_append (&document->objects, &object, 1) == NULL) return 0; @@ -828,7 +831,7 @@ _cairo_pdf_document_update_object (cairo_pdf_document_t *document, cairo_pdf_object_t *object; object = _cairo_array_index (&document->objects, id - 1); - object->offset = ftell (document->file); + object->offset = _cairo_output_stream_get_position (document->output_stream); } static void @@ -899,37 +902,70 @@ _cairo_pdf_surface_add_font (cairo_pdf_surface_t *surface, unsigned int id) _cairo_array_append (&surface->fonts, &resource, 1); } -cairo_surface_t * -cairo_pdf_surface_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) +static cairo_surface_t * +_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height) { cairo_pdf_document_t *document; cairo_surface_t *surface; - document = _cairo_pdf_document_create (file, - width_inches, - height_inches, - x_pixels_per_inch, - y_pixels_per_inch); + document = _cairo_pdf_document_create (stream, width, height); if (document == NULL) return NULL; - surface = _cairo_pdf_surface_create_for_document (document, - width_inches, - height_inches); + surface = _cairo_pdf_surface_create_for_document (document, width, height); + document->owner = surface; _cairo_pdf_document_destroy (document); return surface; } +cairo_surface_t * +cairo_pdf_surface_create_for_stream (cairo_write_func_t write, + void *closure, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write, closure); + if (stream == NULL) + return NULL; + + return _cairo_pdf_surface_create_for_stream_internal (stream, width, height); +} + +cairo_surface_t * +cairo_pdf_surface_create (const char *filename, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_file (filename); + if (stream == NULL) + return NULL; + + return _cairo_pdf_surface_create_for_stream_internal (stream, width, height); +} + +void +cairo_pdf_surface_set_dpi (cairo_surface_t *surface, + double x_dpi, + double y_dpi) +{ + cairo_pdf_surface_t *pdf_surface = (cairo_pdf_surface_t *) surface; + + pdf_surface->document->x_dpi = x_dpi; + pdf_surface->document->y_dpi = y_dpi; +} + static cairo_surface_t * _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, - double width_inches, - double height_inches) + double width, + double height) { cairo_pdf_surface_t *surface; @@ -939,8 +975,8 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, _cairo_surface_init (&surface->base, &cairo_pdf_surface_backend); - surface->width_inches = width_inches; - surface->height_inches = height_inches; + surface->width = width; + surface->height = height; _cairo_pdf_document_reference (document); surface->document = document; @@ -987,10 +1023,12 @@ _cairo_pdf_surface_create_similar (void *abstract_src, static cairo_pdf_stream_t * _cairo_pdf_document_open_stream (cairo_pdf_document_t *document, - const char *extra_entries) + const char *fmt, + ...) { - FILE *file = document->file; + cairo_output_stream_t *output_stream = document->output_stream; cairo_pdf_stream_t *stream; + va_list ap; stream = malloc (sizeof (cairo_pdf_stream_t)); if (stream == NULL) { @@ -1000,17 +1038,23 @@ _cairo_pdf_document_open_stream (cairo_pdf_document_t *document, stream->id = _cairo_pdf_document_new_object (document); stream->length_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Length %d 0 R\r\n" - "%s" - ">>\r\n" - "stream\r\n", - stream->id, - stream->length_id, - extra_entries); + _cairo_output_stream_printf (output_stream, + "%d 0 obj\r\n" + "<< /Length %d 0 R\r\n", + stream->id, + stream->length_id); + + if (fmt != NULL) { + va_start (ap, fmt); + _cairo_output_stream_vprintf (output_stream, fmt, ap); + va_end (ap); + } - stream->start_offset = ftell (file); + _cairo_output_stream_printf (output_stream, + ">>\r\n" + "stream\r\n"); + + stream->start_offset = _cairo_output_stream_get_position (output_stream); document->current_stream = stream; @@ -1020,7 +1064,7 @@ _cairo_pdf_document_open_stream (cairo_pdf_document_t *document, static void _cairo_pdf_document_close_stream (cairo_pdf_document_t *document) { - FILE *file = document->file; + cairo_output_stream_t *output_stream = document->output_stream; long length; cairo_pdf_stream_t *stream; @@ -1028,44 +1072,41 @@ _cairo_pdf_document_close_stream (cairo_pdf_document_t *document) if (stream == NULL) return; - length = ftell(file) - stream->start_offset; - fprintf (file, - "\r\n" - "endstream\r\n" - "endobj\r\n"); + length = _cairo_output_stream_get_position (output_stream) - + stream->start_offset; + _cairo_output_stream_printf (output_stream, + "endstream\r\n" + "endobj\r\n"); _cairo_pdf_document_update_object (document, stream->length_id); - fprintf (file, - "%d 0 obj\r\n" - " %ld\r\n" - "endobj\r\n", - stream->length_id, - length); + _cairo_output_stream_printf (output_stream, + "%d 0 obj\r\n" + " %ld\r\n" + "endobj\r\n", + stream->length_id, + length); document->current_stream = NULL; } -static void -_cairo_pdf_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_pdf_surface_finish (void *abstract_surface) { + cairo_status_t status; cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_document_t *document = surface->document; if (surface->current_stream == document->current_stream) _cairo_pdf_document_close_stream (document); - _cairo_pdf_document_destroy (document); + if (document->owner == &surface->base) + status = _cairo_pdf_document_finish (document); + else + status = CAIRO_STATUS_SUCCESS; - free (surface); -} + _cairo_pdf_document_destroy (document); -/* XXX: We should re-work this interface to return both X/Y ppi values. */ -static double -_cairo_pdf_surface_pixels_per_inch (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - return surface->document->y_ppi; + return status; } static void @@ -1073,71 +1114,29 @@ _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface) { cairo_pdf_document_t *document = surface->document; cairo_pdf_stream_t *stream; - FILE *file = document->file; - char extra[200]; + cairo_output_stream_t *output = document->output_stream; if (document->current_stream == NULL || document->current_stream != surface->current_stream) { _cairo_pdf_document_close_stream (document); - snprintf (extra, sizeof extra, - " /Type /XObject\r\n" - " /Subtype /Form\r\n" - " /BBox [ 0 0 %f %f ]\r\n", - surface->width_inches * document->x_ppi, - surface->height_inches * document->y_ppi); - stream = _cairo_pdf_document_open_stream (document, extra); + stream = _cairo_pdf_document_open_stream (document, + " /Type /XObject\r\n" + " /Subtype /Form\r\n" + " /BBox [ 0 0 %f %f ]\r\n", + surface->width, + surface->height); + _cairo_pdf_surface_add_stream (surface, stream); /* If this is the first stream we open for this surface, * output the cairo to PDF transformation matrix. */ if (_cairo_array_num_elements (&surface->streams) == 1) - fprintf (file, "1 0 0 -1 0 %f cm\r\n", - document->height_inches * document->y_ppi); + _cairo_output_stream_printf (output, + "1 0 0 -1 0 %f cm\r\n", + document->height); } } -static cairo_status_t -_cairo_pdf_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_pdf_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_pdf_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_pdf_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_pdf_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static void * compress_dup (const void *data, unsigned long data_size, unsigned long *compressed_size) @@ -1159,9 +1158,8 @@ static unsigned int emit_image_data (cairo_pdf_document_t *document, cairo_image_surface_t *image) { - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; cairo_pdf_stream_t *stream; - char entries[200]; char *rgb, *compressed; int i, x, y; unsigned long rgb_size, compressed_size; @@ -1191,18 +1189,19 @@ emit_image_data (cairo_pdf_document_t *document, _cairo_pdf_document_close_stream (document); - snprintf (entries, sizeof entries, - " /Type /XObject\r\n" - " /Subtype /Image\r\n" - " /Width %d\r\n" - " /Height %d\r\n" - " /ColorSpace /DeviceRGB\r\n" - " /BitsPerComponent 8\r\n" - " /Filter /FlateDecode\r\n", - image->width, image->height); - - stream = _cairo_pdf_document_open_stream (document, entries); - fwrite (compressed, 1, compressed_size, file); + stream = _cairo_pdf_document_open_stream (document, + " /Type /XObject\r\n" + " /Subtype /Image\r\n" + " /Width %d\r\n" + " /Height %d\r\n" + " /ColorSpace /DeviceRGB\r\n" + " /BitsPerComponent 8\r\n" + " /Filter /FlateDecode\r\n", + image->width, image->height); + + _cairo_output_stream_write (output, compressed, compressed_size); + _cairo_output_stream_printf (output, + "\r\n"); _cairo_pdf_document_close_stream (document); free (rgb); @@ -1216,7 +1215,7 @@ _cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned id; cairo_matrix_t i2u; cairo_status_t status; @@ -1239,17 +1238,17 @@ _cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, _cairo_pdf_surface_ensure_stream (dst); - cairo_matrix_copy (&i2u, &pattern->base.matrix); + i2u = pattern->base.matrix; cairo_matrix_invert (&i2u); cairo_matrix_translate (&i2u, 0, image->height); cairo_matrix_scale (&i2u, image->width, -image->height); - fprintf (file, - "q %f %f %f %f %f %f cm /res%d Do Q\r\n", - i2u.m[0][0], i2u.m[0][1], - i2u.m[1][0], i2u.m[1][1], - i2u.m[2][0], i2u.m[2][1], - id); + _cairo_output_stream_printf (output, + "q %f %f %f %f %f %f cm /res%d Do Q\r\n", + i2u.xx, i2u.yx, + i2u.xy, i2u.yy, + i2u.x0, i2u.y0, + id); bail: _cairo_surface_release_source_image (src, image, image_extra); @@ -1272,7 +1271,7 @@ _cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; cairo_matrix_t i2u; cairo_pdf_stream_t *stream; int num_streams, i; @@ -1282,30 +1281,28 @@ _cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, src = (cairo_pdf_surface_t *) pattern->surface; - cairo_matrix_copy (&i2u, &src->base.matrix); + i2u = src->base.matrix; cairo_matrix_invert (&i2u); - cairo_matrix_scale (&i2u, - 1.0 / (src->width_inches * document->x_ppi), - 1.0 / (src->height_inches * document->y_ppi)); + cairo_matrix_scale (&i2u, 1.0 / src->width, 1.0 / src->height); - fprintf (file, - "q %f %f %f %f %f %f cm", - i2u.m[0][0], i2u.m[0][1], - i2u.m[1][0], i2u.m[1][1], - i2u.m[2][0], i2u.m[2][1]); + _cairo_output_stream_printf (output, + "q %f %f %f %f %f %f cm", + i2u.xx, i2u.yx, + i2u.xy, i2u.yy, + i2u.x0, i2u.y0); num_streams = _cairo_array_num_elements (&src->streams); for (i = 0; i < num_streams; i++) { _cairo_array_copy_element (&src->streams, i, &stream); - fprintf (file, - " /res%d Do", - stream->id); + _cairo_output_stream_printf (output, + " /res%d Do", + stream->id); _cairo_pdf_surface_add_xobject (dst, stream->id); } - fprintf (file, " Q\r\n"); + _cairo_output_stream_printf (output, " Q\r\n"); return CAIRO_STATUS_SUCCESS; } @@ -1348,20 +1345,20 @@ _cairo_pdf_surface_fill_rectangles (void *abstract_surface, { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; int i; _cairo_pdf_surface_ensure_stream (surface); - fprintf (file, - "%f %f %f rg\r\n", - color->red, color->green, color->blue); + _cairo_output_stream_printf (output, + "%f %f %f rg\r\n", + color->red, color->green, color->blue); for (i = 0; i < num_rects; i++) { - fprintf (file, - "%d %d %d %d re f\r\n", - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); + _cairo_output_stream_printf (output, + "%d %d %d %d re f\r\n", + rects[i].x, rects[i].y, + rects[i].width, rects[i].height); } return CAIRO_STATUS_SUCCESS; @@ -1372,17 +1369,17 @@ emit_solid_pattern (cairo_pdf_surface_t *surface, cairo_solid_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int alpha; - alpha = _cairo_pdf_surface_add_alpha (surface, pattern->base.alpha); + alpha = _cairo_pdf_surface_add_alpha (surface, pattern->color.alpha); _cairo_pdf_surface_ensure_stream (surface); - fprintf (file, - "%f %f %f rg /a%d gs\r\n", - pattern->red, - pattern->green, - pattern->blue, - alpha); + _cairo_output_stream_printf (output, + "%f %f %f rg /a%d gs\r\n", + pattern->color.red, + pattern->color.green, + pattern->color.blue, + alpha); } static void @@ -1390,12 +1387,11 @@ emit_surface_pattern (cairo_pdf_surface_t *dst, cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; cairo_pdf_stream_t *stream; cairo_image_surface_t *image; void *image_extra; cairo_status_t status; - char entries[250]; unsigned int id, alpha; cairo_matrix_t pm; @@ -1414,36 +1410,33 @@ emit_surface_pattern (cairo_pdf_surface_t *dst, /* BBox must be smaller than XStep by YStep or acroread wont * display the pattern. */ - cairo_matrix_set_identity (&pm); + cairo_matrix_init_identity (&pm); cairo_matrix_scale (&pm, image->width, image->height); - cairo_matrix_copy (&pm, &pattern->base.matrix); + pm = pattern->base.matrix; cairo_matrix_invert (&pm); - snprintf (entries, sizeof entries, - " /BBox [ 0 0 256 256 ]\r\n" - " /XStep 256\r\n" - " /YStep 256\r\n" - " /PatternType 1\r\n" - " /TilingType 1\r\n" - " /PaintType 1\r\n" - " /Resources << /XObject << /res%d %d 0 R >> >>\r\n" - " /Matrix [ %f %f %f %f %f %f ]\r\n", - id, id, - pm.m[0][0], pm.m[0][1], - pm.m[1][0], pm.m[1][1], - pm.m[2][0], pm.m[2][1]); + stream = _cairo_pdf_document_open_stream (document, + " /BBox [ 0 0 256 256 ]\r\n" + " /XStep 256\r\n" + " /YStep 256\r\n" + " /PatternType 1\r\n" + " /TilingType 1\r\n" + " /PaintType 1\r\n" + " /Resources << /XObject << /res%d %d 0 R >> >>\r\n", + id, id); - stream = _cairo_pdf_document_open_stream (document, entries); - /* FIXME: emit code to show surface here. */ + _cairo_output_stream_printf (output, + " /res%d Do\r\n", + id); _cairo_pdf_surface_add_pattern (dst, stream->id); _cairo_pdf_surface_ensure_stream (dst); alpha = _cairo_pdf_surface_add_alpha (dst, 1.0); - fprintf (file, - "/Pattern cs /res%d scn /a%d gs\r\n", - stream->id, alpha); + _cairo_output_stream_printf (output, + "/Pattern cs /res%d scn /a%d gs\r\n", + stream->id, alpha); _cairo_surface_release_source_image (pattern->surface, image, image_extra); } @@ -1452,33 +1445,37 @@ static unsigned int emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int function_id; + char stops[2][3]; function_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /FunctionType 0\r\n" - " /Domain [ 0.0 1.0 ]\r\n" - " /Size [ 2 ]\r\n" - " /BitsPerSample 8\r\n" - " /Range [ 0.0 1.0 0.0 1.0 0.0 1.0 ]\r\n" - " /Length 6\r\n" - ">>\r\n" - "stream\r\n", - function_id); - - fputc (pattern->stops[0].color.red * 0xff, file); - fputc (pattern->stops[0].color.green * 0xff, file); - fputc (pattern->stops[0].color.blue * 0xff, file); - fputc (pattern->stops[1].color.red * 0xff, file); - fputc (pattern->stops[1].color.green * 0xff, file); - fputc (pattern->stops[1].color.blue * 0xff, file); - - fprintf (file, - "\r\n" - "endstream\r\n" - "endobj\r\n"); + + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /FunctionType 0\r\n" + " /Domain [ 0.0 1.0 ]\r\n" + " /Size [ 2 ]\r\n" + " /BitsPerSample 8\r\n" + " /Range [ 0.0 1.0 0.0 1.0 0.0 1.0 ]\r\n" + " /Length 6\r\n" + ">>\r\n" + "stream\r\n", + function_id); + + stops[0][0] = pattern->stops[0].color.red * 0xff + 0.5; + stops[0][1] = pattern->stops[0].color.green * 0xff + 0.5; + stops[0][2] = pattern->stops[0].color.blue * 0xff + 0.5; + stops[1][0] = pattern->stops[1].color.red * 0xff + 0.5; + stops[1][1] = pattern->stops[1].color.green * 0xff + 0.5; + stops[1][2] = pattern->stops[1].color.blue * 0xff + 0.5; + + _cairo_output_stream_write (output, stops, sizeof (stops)); + + _cairo_output_stream_printf (output, + "\r\n" + "endstream\r\n" + "endobj\r\n"); return function_id; } @@ -1487,7 +1484,7 @@ static void emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int function_id, pattern_id, alpha; double x0, y0, x1, y1; cairo_matrix_t p2u; @@ -1496,7 +1493,7 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *patte function_id = emit_pattern_stops (surface, &pattern->base); - cairo_matrix_copy (&p2u, &pattern->base.base.matrix); + p2u = pattern->base.base.matrix; cairo_matrix_invert (&p2u); x0 = pattern->point0.x; @@ -1507,24 +1504,24 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *patte cairo_matrix_transform_point (&p2u, &x1, &y1); pattern_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Pattern\r\n" - " /PatternType 2\r\n" - " /Matrix [ 1 0 0 -1 0 %f ]\r\n" - " /Shading\r\n" - " << /ShadingType 2\r\n" - " /ColorSpace /DeviceRGB\r\n" - " /Coords [ %f %f %f %f ]\r\n" - " /Function %d 0 R\r\n" - " /Extend [ true true ]\r\n" - " >>\r\n" - ">>\r\n" - "endobj\r\n", - pattern_id, - document->height_inches * document->y_ppi, - x0, y0, x1, y1, - function_id); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Type /Pattern\r\n" + " /PatternType 2\r\n" + " /Matrix [ 1 0 0 -1 0 %f ]\r\n" + " /Shading\r\n" + " << /ShadingType 2\r\n" + " /ColorSpace /DeviceRGB\r\n" + " /Coords [ %f %f %f %f ]\r\n" + " /Function %d 0 R\r\n" + " /Extend [ true true ]\r\n" + " >>\r\n" + ">>\r\n" + "endobj\r\n", + pattern_id, + document->height, + x0, y0, x1, y1, + function_id); _cairo_pdf_surface_add_pattern (surface, pattern_id); @@ -1532,16 +1529,16 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *patte alpha = _cairo_pdf_surface_add_alpha (surface, 1.0); /* Use pattern */ - fprintf (file, - "/Pattern cs /res%d scn /a%d gs\r\n", - pattern_id, alpha); + _cairo_output_stream_printf (output, + "/Pattern cs /res%d scn /a%d gs\r\n", + pattern_id, alpha); } static void emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int function_id, pattern_id, alpha; double x0, y0, x1, y1, r0, r1; cairo_matrix_t p2u; @@ -1550,7 +1547,7 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *patte function_id = emit_pattern_stops (surface, &pattern->base); - cairo_matrix_copy (&p2u, &pattern->base.base.matrix); + p2u = pattern->base.base.matrix; cairo_matrix_invert (&p2u); x0 = pattern->center0.x; @@ -1574,24 +1571,24 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *patte * behavoir, not yet sure how to implement the cairo mirror and * repeat behaviour. */ pattern_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Pattern\r\n" - " /PatternType 2\r\n" - " /Matrix [ 1 0 0 -1 0 %f ]\r\n" - " /Shading\r\n" - " << /ShadingType 3\r\n" - " /ColorSpace /DeviceRGB\r\n" - " /Coords [ %f %f %f %f %f %f ]\r\n" - " /Function %d 0 R\r\n" - " /Extend [ true true ]\r\n" - " >>\r\n" - ">>\r\n" - "endobj\r\n", - pattern_id, - document->height_inches * document->y_ppi, - x0, y0, r0, x1, y1, r1, - function_id); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Type /Pattern\r\n" + " /PatternType 2\r\n" + " /Matrix [ 1 0 0 -1 0 %f ]\r\n" + " /Shading\r\n" + " << /ShadingType 3\r\n" + " /ColorSpace /DeviceRGB\r\n" + " /Coords [ %f %f %f %f %f %f ]\r\n" + " /Function %d 0 R\r\n" + " /Extend [ true true ]\r\n" + " >>\r\n" + ">>\r\n" + "endobj\r\n", + pattern_id, + document->height, + x0, y0, r0, x1, y1, r1, + function_id); _cairo_pdf_surface_add_pattern (surface, pattern_id); @@ -1599,9 +1596,9 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *patte alpha = _cairo_pdf_surface_add_alpha (surface, 1.0); /* Use pattern */ - fprintf (file, - "/Pattern cs /res%d scn /a%d gs\r\n", - pattern_id, alpha); + _cairo_output_stream_printf (output, + "/Pattern cs /res%d scn /a%d gs\r\n", + pattern_id, alpha); } static void @@ -1635,6 +1632,104 @@ intersect (cairo_line_t *line, cairo_fixed_t y) _cairo_fixed_to_double (line->p2.y - line->p1.y); } +typedef struct +{ + cairo_output_stream_t *output_stream; +} pdf_path_info_t; + +static cairo_status_t +_cairo_pdf_path_move_to (void *closure, cairo_point_t *point) +{ + pdf_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "%f %f m ", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_path_line_to (void *closure, cairo_point_t *point) +{ + pdf_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "%f %f l ", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_path_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) +{ + pdf_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "%f %f %f %f %f %f c ", + _cairo_fixed_to_double (b->x), + _cairo_fixed_to_double (b->y), + _cairo_fixed_to_double (c->x), + _cairo_fixed_to_double (c->y), + _cairo_fixed_to_double (d->x), + _cairo_fixed_to_double (d->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_path_close_path (void *closure) +{ + pdf_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output_stream, + "h\r\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_fill_path (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + cairo_path_fixed_t *path) +{ + cairo_pdf_surface_t *surface = abstract_dst; + cairo_pdf_document_t *document = surface->document; + cairo_status_t status; + pdf_path_info_t info; + + return CAIRO_INT_STATUS_UNSUPPORTED; + + emit_pattern (surface, pattern); + + /* After the above switch the current stream should belong to this + * surface, so no need to _cairo_pdf_surface_ensure_stream() */ + assert (document->current_stream != NULL && + document->current_stream == surface->current_stream); + + info.output_stream = document->output_stream; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_pdf_path_move_to, + _cairo_pdf_path_line_to, + _cairo_pdf_path_curve_to, + _cairo_pdf_path_close_path, + &info); + + _cairo_output_stream_printf (document->output_stream, + "f\r\n"); + + return status; +} + static cairo_int_status_t _cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator, cairo_pattern_t *pattern, @@ -1650,7 +1745,7 @@ _cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator, { cairo_pdf_surface_t *surface = abstract_dst; cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; int i; emit_pattern (surface, pattern); @@ -1668,16 +1763,16 @@ _cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator, right_x1 = intersect (&traps[i].right, traps[i].top); right_x2 = intersect (&traps[i].right, traps[i].bottom); - fprintf (file, - "%f %f m %f %f l %f %f l %f %f l h\r\n", - left_x1, _cairo_fixed_to_double (traps[i].top), - left_x2, _cairo_fixed_to_double (traps[i].bottom), - right_x2, _cairo_fixed_to_double (traps[i].bottom), - right_x1, _cairo_fixed_to_double (traps[i].top)); + _cairo_output_stream_printf (output, + "%f %f m %f %f l %f %f l %f %f l h\r\n", + left_x1, _cairo_fixed_to_double (traps[i].top), + left_x2, _cairo_fixed_to_double (traps[i].bottom), + right_x2, _cairo_fixed_to_double (traps[i].bottom), + right_x1, _cairo_fixed_to_double (traps[i].top)); } - fprintf (file, - "f\r\n"); + _cairo_output_stream_printf (output, + "f\r\n"); return CAIRO_STATUS_SUCCESS; } @@ -1706,21 +1801,33 @@ _cairo_pdf_surface_show_page (void *abstract_surface) } static cairo_int_status_t -_cairo_pdf_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) +_cairo_pdf_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) { - return CAIRO_INT_STATUS_UNSUPPORTED; + cairo_pdf_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + /* XXX: The conversion to integers here is pretty bogus, (not to + * mention the aribitray limitation of width to a short(!). We + * may need to come up with a better interface for get_size. + */ + rectangle->width = (int) ceil (surface->width); + rectangle->height = (int) ceil (surface->height); + + return CAIRO_STATUS_SUCCESS; } static cairo_pdf_font_t * _cairo_pdf_document_get_font (cairo_pdf_document_t *document, - cairo_font_t *font) + cairo_scaled_font_t *scaled_font) { cairo_unscaled_font_t *unscaled_font; cairo_pdf_font_t *pdf_font; unsigned int num_fonts, i; - unscaled_font = _cairo_ft_font_get_unscaled_font (font); + unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font); num_fonts = _cairo_array_num_elements (&document->fonts); for (i = 0; i < num_fonts; i++) { @@ -1743,8 +1850,8 @@ _cairo_pdf_document_get_font (cairo_pdf_document_t *document, return pdf_font; } -static cairo_status_t -_cairo_pdf_surface_show_glyphs (cairo_font_t *font, +static cairo_int_status_t +_cairo_pdf_surface_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, cairo_pattern_t *pattern, void *abstract_surface, @@ -1759,32 +1866,34 @@ _cairo_pdf_surface_show_glyphs (cairo_font_t *font, { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; cairo_pdf_font_t *pdf_font; int i, index; - pdf_font = _cairo_pdf_document_get_font (document, font); + pdf_font = _cairo_pdf_document_get_font (document, scaled_font); if (pdf_font == NULL) return CAIRO_STATUS_NO_MEMORY; emit_pattern (surface, pattern); - fprintf (file, "BT /res%u 1 Tf", pdf_font->font_id); + _cairo_output_stream_printf (output, + "BT /res%u 1 Tf", pdf_font->font_id); for (i = 0; i < num_glyphs; i++) { index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); - fprintf (file, - " %f %f %f %f %f %f Tm (\\%o) Tj", - font->scale.matrix[0][0], - font->scale.matrix[0][1], - font->scale.matrix[1][0], - -font->scale.matrix[1][1], - glyphs[i].x, - glyphs[i].y, - index); + _cairo_output_stream_printf (output, + " %f %f %f %f %f %f Tm (\\%o) Tj", + scaled_font->scale.xx, + scaled_font->scale.yx, + scaled_font->scale.xy, + -scaled_font->scale.yy, + glyphs[i].x, + glyphs[i].y, + index); } - fprintf (file, " ET\r\n"); + _cairo_output_stream_printf (output, + " ET\r\n"); _cairo_pdf_surface_add_font (surface, pdf_font->font_id); @@ -1793,28 +1902,27 @@ _cairo_pdf_surface_show_glyphs (cairo_font_t *font, static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_create_similar, - _cairo_pdf_surface_destroy, - _cairo_pdf_surface_pixels_per_inch, - _cairo_pdf_surface_acquire_source_image, - _cairo_pdf_surface_release_source_image, - _cairo_pdf_surface_acquire_dest_image, - _cairo_pdf_surface_release_dest_image, - _cairo_pdf_surface_clone_similar, + _cairo_pdf_surface_finish, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ _cairo_pdf_surface_composite, _cairo_pdf_surface_fill_rectangles, _cairo_pdf_surface_composite_trapezoids, _cairo_pdf_surface_copy_page, _cairo_pdf_surface_show_page, - _cairo_pdf_surface_set_clip_region, - _cairo_pdf_surface_show_glyphs + NULL, /* set_clip_region */ + _cairo_pdf_surface_get_extents, + _cairo_pdf_surface_show_glyphs, + _cairo_pdf_surface_fill_path }; static cairo_pdf_document_t * -_cairo_pdf_document_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) +_cairo_pdf_document_create (cairo_output_stream_t *output_stream, + double width, + double height) { cairo_pdf_document_t *document; @@ -1822,12 +1930,14 @@ _cairo_pdf_document_create (FILE *file, if (document == NULL) return NULL; - document->file = file; + document->output_stream = output_stream; document->refcount = 1; - document->width_inches = width_inches; - document->height_inches = height_inches; - document->x_ppi = x_pixels_per_inch; - document->y_ppi = y_pixels_per_inch; + document->owner = NULL; + document->finished = FALSE; + document->width = width; + document->height = height; + document->x_dpi = DEFAULT_DPI; + document->y_dpi = DEFAULT_DPI; _cairo_array_init (&document->objects, sizeof (cairo_pdf_object_t)); _cairo_array_init (&document->pages, sizeof (unsigned int)); @@ -1840,7 +1950,8 @@ _cairo_pdf_document_create (FILE *file, _cairo_array_init (&document->fonts, sizeof (cairo_pdf_font_t *)); /* Document header */ - fprintf (file, "%%PDF-1.4\r\n"); + _cairo_output_stream_printf (output_stream, + "%%PDF-1.4\r\n"); return document; } @@ -1848,17 +1959,17 @@ _cairo_pdf_document_create (FILE *file, static unsigned int _cairo_pdf_document_write_info (cairo_pdf_document_t *document) { - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int id; id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Creator (cairographics.org)\r\n" - " /Producer (cairographics.org)\r\n" - ">>\r\n" - "endobj\r\n", - id); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Creator (cairographics.org)\r\n" + " /Producer (cairographics.org)\r\n" + ">>\r\n" + "endobj\r\n", + id); return id; } @@ -1866,40 +1977,40 @@ _cairo_pdf_document_write_info (cairo_pdf_document_t *document) static void _cairo_pdf_document_write_pages (cairo_pdf_document_t *document) { - FILE *file = document->file; + cairo_output_stream_t *stream = document->output_stream; unsigned int page_id; int num_pages, i; _cairo_pdf_document_update_object (document, document->pages_id); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Pages\r\n" - " /Kids [ ", - document->pages_id); + _cairo_output_stream_printf (stream, + "%d 0 obj\r\n" + "<< /Type /Pages\r\n" + " /Kids [ ", + document->pages_id); num_pages = _cairo_array_num_elements (&document->pages); for (i = 0; i < num_pages; i++) { _cairo_array_copy_element (&document->pages, i, &page_id); - fprintf (file, "%d 0 R ", page_id); + _cairo_output_stream_printf (stream, "%d 0 R ", page_id); } - fprintf (file, "]\r\n"); - fprintf (file, " /Count %d\r\n", num_pages); + _cairo_output_stream_printf (stream, "]\r\n"); + _cairo_output_stream_printf (stream, " /Count %d\r\n", num_pages); /* TODO: Figure out wich other defaults to be inherited by /Page * objects. */ - fprintf (file, - " /MediaBox [ 0 0 %f %f ]\r\n" - ">>\r\n" - "endobj\r\n", - document->width_inches * document->x_ppi, - document->height_inches * document->y_ppi); + _cairo_output_stream_printf (stream, + " /MediaBox [ 0 0 %f %f ]\r\n" + ">>\r\n" + "endobj\r\n", + document->width, + document->height); } static cairo_status_t _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) { - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; cairo_pdf_font_t *font; int num_fonts, i, j; const char *data; @@ -1923,76 +2034,76 @@ _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) } stream_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Filter /FlateDecode\r\n" - " /Length %lu\r\n" - " /Length1 %lu\r\n" - ">>\r\n" - "stream\r\n", - stream_id, - compressed_size, - data_size); - fwrite (compressed, 1, compressed_size, file); - fprintf (file, - "\r\n" - "endstream\r\n" - "endobj\r\n"); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Filter /FlateDecode\r\n" + " /Length %lu\r\n" + " /Length1 %lu\r\n" + ">>\r\n" + "stream\r\n", + stream_id, + compressed_size, + data_size); + _cairo_output_stream_write (output, compressed, compressed_size); + _cairo_output_stream_printf (output, + "\r\n" + "endstream\r\n" + "endobj\r\n"); free (compressed); descriptor_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /FontDescriptor\r\n" - " /FontName /7%s\r\n" - " /Flags 4\r\n" - " /FontBBox [ %ld %ld %ld %ld ]\r\n" - " /ItalicAngle 0\r\n" - " /Ascent %ld\r\n" - " /Descent %ld\r\n" - " /CapHeight 500\r\n" - " /StemV 80\r\n" - " /StemH 80\r\n" - " /FontFile2 %u 0 R\r\n" - ">>\r\n" - "endobj\r\n", - descriptor_id, - font->base_font, - font->x_min, - font->y_min, - font->x_max, - font->y_max, - font->ascent, - font->descent, - stream_id); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Type /FontDescriptor\r\n" + " /FontName /7%s\r\n" + " /Flags 4\r\n" + " /FontBBox [ %ld %ld %ld %ld ]\r\n" + " /ItalicAngle 0\r\n" + " /Ascent %ld\r\n" + " /Descent %ld\r\n" + " /CapHeight 500\r\n" + " /StemV 80\r\n" + " /StemH 80\r\n" + " /FontFile2 %u 0 R\r\n" + ">>\r\n" + "endobj\r\n", + descriptor_id, + font->base_font, + font->x_min, + font->y_min, + font->x_max, + font->y_max, + font->ascent, + font->descent, + stream_id); _cairo_pdf_document_update_object (document, font->font_id); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Font\r\n" - " /Subtype /TrueType\r\n" - " /BaseFont /%s\r\n" - " /FirstChar 0\r\n" - " /LastChar %d\r\n" - " /FontDescriptor %d 0 R\r\n" - " /Widths ", - font->font_id, - font->base_font, - font->num_glyphs, - descriptor_id); - - fprintf (file, - "["); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Type /Font\r\n" + " /Subtype /TrueType\r\n" + " /BaseFont /%s\r\n" + " /FirstChar 0\r\n" + " /LastChar %d\r\n" + " /FontDescriptor %d 0 R\r\n" + " /Widths ", + font->font_id, + font->base_font, + font->num_glyphs, + descriptor_id); + + _cairo_output_stream_printf (output, + "["); for (j = 0; j < font->num_glyphs; j++) - fprintf (file, - " %d", - font->widths[j]); + _cairo_output_stream_printf (output, + " %d", + font->widths[j]); - fprintf (file, - " ]\r\n" - ">>\r\n" - "endobj\r\n"); + _cairo_output_stream_printf (output, + " ]\r\n" + ">>\r\n" + "endobj\r\n"); fail: cairo_pdf_ft_font_destroy (font); @@ -2004,17 +2115,17 @@ _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) static unsigned int _cairo_pdf_document_write_catalog (cairo_pdf_document_t *document) { - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int id; id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Catalog\r\n" - " /Pages %d 0 R\r\n" - ">>\r\n" - "endobj\r\n", - id, document->pages_id); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Type /Catalog\r\n" + " /Pages %d 0 R\r\n" + ">>\r\n" + "endobj\r\n", + id, document->pages_id); return id; } @@ -2022,23 +2133,27 @@ _cairo_pdf_document_write_catalog (cairo_pdf_document_t *document) static long _cairo_pdf_document_write_xref (cairo_pdf_document_t *document) { - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; cairo_pdf_object_t *object; int num_objects, i; long offset; + char buffer[11]; num_objects = _cairo_array_num_elements (&document->objects); - offset = ftell(file); - fprintf (document->file, - "xref\r\n" - "%d %d\r\n", - 0, num_objects + 1); + offset = _cairo_output_stream_get_position (output); + _cairo_output_stream_printf (output, + "xref\r\n" + "%d %d\r\n", + 0, num_objects + 1); - fprintf (file, "0000000000 65535 f\r\n"); + _cairo_output_stream_printf (output, + "0000000000 65535 f\r\n"); for (i = 0; i < num_objects; i++) { object = _cairo_array_index (&document->objects, i); - fprintf (file, "%010ld 00000 n\r\n", object->offset); + snprintf (buffer, sizeof buffer, "%010ld", object->offset); + _cairo_output_stream_printf (output, + "%s 00000 n\r\n", buffer); } return offset; @@ -2053,14 +2168,26 @@ _cairo_pdf_document_reference (cairo_pdf_document_t *document) static void _cairo_pdf_document_destroy (cairo_pdf_document_t *document) { - FILE *file = document->file; - long offset; - unsigned int info_id, catalog_id; - document->refcount--; if (document->refcount > 0) return; + _cairo_pdf_document_finish (document); + + free (document); +} + +static cairo_status_t +_cairo_pdf_document_finish (cairo_pdf_document_t *document) +{ + cairo_status_t status; + cairo_output_stream_t *output = document->output_stream; + long offset; + unsigned int info_id, catalog_id; + + if (document->finished) + return CAIRO_STATUS_SUCCESS; + _cairo_pdf_document_close_stream (document); _cairo_pdf_document_write_pages (document); _cairo_pdf_document_write_fonts (document); @@ -2068,23 +2195,28 @@ _cairo_pdf_document_destroy (cairo_pdf_document_t *document) catalog_id = _cairo_pdf_document_write_catalog (document); offset = _cairo_pdf_document_write_xref (document); - fprintf (file, - "trailer\r\n" - "<< /Size %d\r\n" - " /Root %d 0 R\r\n" - " /Info %d 0 R\r\n" - ">>\r\n", - document->next_available_id, - catalog_id, - info_id); - - fprintf (file, - "startxref\r\n" - "%ld\r\n" - "%%%%EOF\r\n", - offset); + _cairo_output_stream_printf (output, + "trailer\r\n" + "<< /Size %d\r\n" + " /Root %d 0 R\r\n" + " /Info %d 0 R\r\n" + ">>\r\n", + document->next_available_id, + catalog_id, + info_id); + + _cairo_output_stream_printf (output, + "startxref\r\n" + "%ld\r\n" + "%%%%EOF\r\n", + offset); + + status = _cairo_output_stream_get_status (output); + _cairo_output_stream_destroy (output); + + document->finished = TRUE; - free (document); + return status; } static cairo_status_t @@ -2093,130 +2225,105 @@ _cairo_pdf_document_add_page (cairo_pdf_document_t *document, { cairo_pdf_stream_t *stream; cairo_pdf_resource_t *res; - FILE *file = document->file; + cairo_output_stream_t *output = document->output_stream; unsigned int page_id; double alpha; int num_streams, num_alphas, num_resources, i; + assert (!document->finished); + _cairo_pdf_document_close_stream (document); page_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Page\r\n" - " /Parent %d 0 R\r\n" - " /Contents [", - page_id, - document->pages_id); + _cairo_output_stream_printf (output, + "%d 0 obj\r\n" + "<< /Type /Page\r\n" + " /Parent %d 0 R\r\n" + " /Contents [", + page_id, + document->pages_id); num_streams = _cairo_array_num_elements (&surface->streams); for (i = 0; i < num_streams; i++) { _cairo_array_copy_element (&surface->streams, i, &stream); - fprintf (file, - " %d 0 R", - stream->id); + _cairo_output_stream_printf (output, + " %d 0 R", + stream->id); } - fprintf (file, - " ]\r\n" - " /Resources <<\r\n"); + _cairo_output_stream_printf (output, + " ]\r\n" + " /Resources <<\r\n"); num_resources = _cairo_array_num_elements (&surface->fonts); if (num_resources > 0) { - fprintf (file, - " /Font <<"); + _cairo_output_stream_printf (output, + " /Font <<"); for (i = 0; i < num_resources; i++) { res = _cairo_array_index (&surface->fonts, i); - fprintf (file, - " /res%d %d 0 R", - res->id, res->id); + _cairo_output_stream_printf (output, + " /res%d %d 0 R", + res->id, res->id); } - fprintf (file, - " >>\r\n"); + _cairo_output_stream_printf (output, + " >>\r\n"); } num_alphas = _cairo_array_num_elements (&surface->alphas); if (num_alphas > 0) { - fprintf (file, - " /ExtGState <<\r\n"); + _cairo_output_stream_printf (output, + " /ExtGState <<\r\n"); for (i = 0; i < num_alphas; i++) { _cairo_array_copy_element (&surface->alphas, i, &alpha); - fprintf (file, - " /a%d << /ca %f >>\r\n", - i, alpha); + _cairo_output_stream_printf (output, + " /a%d << /ca %f >>\r\n", + i, alpha); } - fprintf (file, - " >>\r\n"); + _cairo_output_stream_printf (output, + " >>\r\n"); } num_resources = _cairo_array_num_elements (&surface->patterns); if (num_resources > 0) { - fprintf (file, - " /Pattern <<"); + _cairo_output_stream_printf (output, + " /Pattern <<"); for (i = 0; i < num_resources; i++) { res = _cairo_array_index (&surface->patterns, i); - fprintf (file, - " /res%d %d 0 R", - res->id, res->id); + _cairo_output_stream_printf (output, + " /res%d %d 0 R", + res->id, res->id); } - fprintf (file, - " >>\r\n"); + _cairo_output_stream_printf (output, + " >>\r\n"); } num_resources = _cairo_array_num_elements (&surface->xobjects); if (num_resources > 0) { - fprintf (file, - " /XObject <<"); + _cairo_output_stream_printf (output, + " /XObject <<"); for (i = 0; i < num_resources; i++) { res = _cairo_array_index (&surface->xobjects, i); - fprintf (file, - " /res%d %d 0 R", - res->id, res->id); + _cairo_output_stream_printf (output, + " /res%d %d 0 R", + res->id, res->id); } - fprintf (file, - " >>\r\n"); + _cairo_output_stream_printf (output, + " >>\r\n"); } - fprintf (file, - " >>\r\n" - ">>\r\n" - "endobj\r\n"); + _cairo_output_stream_printf (output, + " >>\r\n" + ">>\r\n" + "endobj\r\n"); _cairo_array_append (&document->pages, &page_id, 1); return CAIRO_STATUS_SUCCESS; } - -void -cairo_set_target_pdf (cairo_t *cr, - FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_surface_t *surface; - - surface = cairo_pdf_surface_create (file, - width_inches, - height_inches, - x_pixels_per_inch, - y_pixels_per_inch); - - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 701a7b4a7..61bf7a841 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -39,29 +39,30 @@ #include <cairo.h> -#ifdef CAIRO_HAS_PDF_SURFACE - -#include <stdio.h> +#if CAIRO_HAS_PDF_SURFACE CAIRO_BEGIN_DECLS -void -cairo_set_target_pdf (cairo_t *cr, - FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch); - +cairo_surface_t * +cairo_pdf_surface_create (const char *filename, + double width_in_points, + double height_in_points); cairo_surface_t * -cairo_pdf_surface_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch); +cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +void +cairo_pdf_surface_set_dpi (cairo_surface_t *surface, + double x_dpi, + double y_dpi); CAIRO_END_DECLS +#else /* CAIRO_HAS_PDF_SURFACE */ +# error Cairo was not compiled with support for the pdf backend #endif /* CAIRO_HAS_PDF_SURFACE */ + #endif /* CAIRO_PDF_H */ diff --git a/src/cairo-pen.c b/src/cairo-pen.c index 6ecaa00b3..18b9ddb59 100644 --- a/src/cairo-pen.c +++ b/src/cairo-pen.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-gstate-private.h" + static int _cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix); @@ -372,8 +374,8 @@ _cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix) { - double a = matrix->m[0][0], c = matrix->m[0][1]; - double b = matrix->m[1][0], d = matrix->m[1][1]; + double a = matrix->xx, b = matrix->yx; + double c = matrix->xy, d = matrix->yy; double i = a*a + c*c; double j = b*b + d*d; diff --git a/src/cairo-png.c b/src/cairo-png.c new file mode 100644 index 000000000..ecb23ca45 --- /dev/null +++ b/src/cairo-png.c @@ -0,0 +1,465 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 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. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth <cworth@cworth.org> + * Kristian Høgsberg <krh@redhat.com> + */ + +#include <png.h> +#include "cairoint.h" + +static void +unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + uint8_t alpha; + + memcpy (&pixel, b, sizeof (uint32_t)); + alpha = (pixel & 0xff000000) >> 24; + if (alpha == 0) { + b[0] = b[1] = b[2] = b[3] = 0; + } else { + b[0] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + b[2] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + b[3] = alpha; + } + } +} + +static cairo_status_t +write_png (cairo_surface_t *surface, + png_rw_ptr write_func, + void *closure) +{ + int i; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_image_surface_t *image; + void *image_extra; + png_struct *png; + png_info *info; + png_time pt; + png_byte **rows; + png_color_16 white; + int png_color_type; + int depth; + + status = _cairo_surface_acquire_source_image (surface, + &image, + &image_extra); + + if (status == CAIRO_STATUS_NO_MEMORY) + return CAIRO_STATUS_NO_MEMORY; + else if (status != CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + rows = malloc (image->height * sizeof(png_byte*)); + if (rows == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto BAIL1; + } + + for (i = 0; i < image->height; i++) + rows[i] = (png_byte *) image->data + i * image->stride; + + png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto BAIL2; + } + + info = png_create_info_struct (png); + if (info == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto BAIL3; + } + + if (setjmp (png_jmpbuf (png))) { + status = CAIRO_STATUS_NO_MEMORY; + goto BAIL3; + } + + png_set_write_fn (png, closure, write_func, NULL); + + switch (image->format) { + case CAIRO_FORMAT_ARGB32: + depth = 8; + png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case CAIRO_FORMAT_RGB24: + depth = 8; + png_color_type = PNG_COLOR_TYPE_RGB; + break; + case CAIRO_FORMAT_A8: + depth = 8; + png_color_type = PNG_COLOR_TYPE_GRAY; + break; + case CAIRO_FORMAT_A1: + depth = 1; + png_color_type = PNG_COLOR_TYPE_GRAY; + break; + default: + status = CAIRO_STATUS_NULL_POINTER; + goto BAIL3; + } + + png_set_IHDR (png, info, + image->width, + image->height, depth, + png_color_type, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + white.red = 0xff; + white.blue = 0xff; + white.green = 0xff; + png_set_bKGD (png, info, &white); + + png_convert_from_time_t (&pt, time (NULL)); + png_set_tIME (png, info, &pt); + + png_set_write_user_transform_fn (png, unpremultiply_data); + if (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_RGB24) + png_set_bgr (png); + if (image->format == CAIRO_FORMAT_RGB24) + png_set_filler (png, 0, PNG_FILLER_AFTER); + + png_write_info (png, info); + png_write_image (png, rows); + png_write_end (png, info); + +BAIL3: + png_destroy_write_struct (&png, &info); +BAIL2: + free (rows); +BAIL1: + _cairo_surface_release_source_image (surface, image, image_extra); + + return status; +} + +static void +stdio_write_func (png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp; + + fp = png_get_io_ptr (png); + if (fwrite (data, 1, size, fp) != size) + png_error(png, "Write Error"); +} + +/** + * cairo_surface_write_to_png: + * @surface: a #cairo_surface_t with pixel contents + * @filename: the name of a file to write to + * + * Writes the contents of @surface to a new file @filename as a PNG + * image. + * + * Return value: CAIRO_STATUS_SUCCESS if the PNG file was written + * successfully. Otherwise, CAIRO_STATUS_NO_MEMORY if memory could not + * be allocated for the operation or + * CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have + * pixel contents, or CAIRO_STATUS_WRITE_ERROR if an I/O error occurs + * while attempting to write the file. + **/ +cairo_status_t +cairo_surface_write_to_png (cairo_surface_t *surface, + const char *filename) +{ + FILE *fp; + cairo_status_t status; + + fp = fopen (filename, "wb"); + if (fp == NULL) + return CAIRO_STATUS_WRITE_ERROR; + + status = write_png (surface, stdio_write_func, fp); + + if (fclose (fp) && CAIRO_OK (status)) + status = CAIRO_STATUS_WRITE_ERROR; + + return status; +} + +struct png_write_closure_t { + cairo_write_func_t write_func; + void *closure; +}; + +static void +stream_write_func (png_structp png, png_bytep data, png_size_t size) +{ + struct png_write_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + if (!CAIRO_OK (png_closure->write_func (png_closure->closure, data, size))) + png_error(png, "Write Error"); +} + +/** + * cairo_surface_write_to_png_stream: + * @surface: a #cairo_surface_t with pixel contents + * @write_func: a #cairo_write_func_t + * @closure: closure data for the write function + * + * Writes the image surface to the write function. + * + * Return value: CAIRO_STATUS_SUCCESS if the PNG file was written + * successfully. Otherwise, CAIRO_STATUS_NO_MEMORY is returned if + * memory could not be allocated for the operation, + * CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have + * pixel contents. + **/ +cairo_status_t +cairo_surface_write_to_png_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure) +{ + struct png_write_closure_t png_closure; + + png_closure.write_func = write_func; + png_closure.closure = closure; + + return write_png (surface, stream_write_func, &png_closure); +} + +static void +premultiply_data (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t blue = base[0]; + uint8_t green = base[1]; + uint8_t red = base[2]; + uint8_t alpha = base[3]; + uint32_t p; + + red = ((unsigned) red * (unsigned) alpha + 127) / 255; + green = ((unsigned) green * (unsigned) alpha + 127) / 255; + blue = ((unsigned) blue * (unsigned) alpha + 127) / 255; + p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &p, sizeof (uint32_t)); + } +} + +static cairo_surface_t * +read_png (png_rw_ptr read_func, + void *closure) +{ + cairo_surface_t *surface; + png_byte *data; + int i; + png_struct *png; + png_info *info; + png_uint_32 png_width, png_height, stride; + int depth, color_type, interlace; + unsigned int pixel_size; + png_byte **row_pointers; + + surface = NULL; + data = NULL; + row_pointers = NULL; + + /* XXX: Perhaps we'll want some other error handlers? */ + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if (png == NULL) + return NULL; + + info = png_create_info_struct (png); + if (info == NULL) + goto BAIL; + + png_set_read_fn (png, closure, read_func); + + if (setjmp (png_jmpbuf (png))) + goto BAIL; + + png_read_info (png, info); + + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + stride = 4 * png_width; + + /* convert palette/gray image to rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + /* expand gray bit depth if needed */ + if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8) + png_set_gray_1_2_4_to_8 (png); + /* transform transparency to alpha */ + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth == 16) + png_set_strip_16 (png); + + if (depth < 8) + png_set_packing (png); + + /* convert grayscale to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY + || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png); + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_set_bgr (png); + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + png_set_read_user_transform_fn (png, premultiply_data); + + png_read_update_info (png, info); + + pixel_size = 4; + data = malloc (png_width * png_height * pixel_size); + if (data == NULL) + goto BAIL; + + row_pointers = malloc (png_height * sizeof(char *)); + if (row_pointers == NULL) + goto BAIL; + + for (i = 0; i < png_height; i++) + row_pointers[i] = &data[i * png_width * pixel_size]; + + png_read_image (png, row_pointers); + png_read_end (png, info); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + png_width, png_height, stride); + _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface); + data = NULL; + + BAIL: + free (row_pointers); + free (data); + png_destroy_read_struct (&png, NULL, NULL); + + return surface; +} + +static void +stdio_read_func (png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp; + + fp = png_get_io_ptr (png); + if (fread (data, 1, size, fp) != size) + png_error(png, "Read Error"); +} + +/** + * cairo_image_surface_create_from_png: + * @filename: name of PNG file to load + * + * Creates a new image surface and initializes the contents to the + * given PNG file. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file or %NULL if the file is not a valid PNG file or + * memory could not be allocated for the operation. + **/ +cairo_surface_t * +cairo_image_surface_create_from_png (const char *filename) +{ + FILE *fp; + cairo_surface_t *surface; + + fp = fopen (filename, "rb"); + if (fp == NULL) + return NULL; + + surface = read_png (stdio_read_func, fp); + + fclose (fp); + + return surface; +} + +struct png_read_closure_t { + cairo_read_func_t read_func; + void *closure; +}; + +static void +stream_read_func (png_structp png, png_bytep data, png_size_t size) +{ + struct png_read_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + if (!CAIRO_OK (png_closure->read_func (png_closure->closure, data, size))) + png_error(png, "Read Error"); +} + +/** + * cairo_image_surface_create_from_png_stream: + * @read_func: function called to read the data of the file + * @closure: data to pass to @read_func. + * + * Creates a new image surface from PNG data read incrementally + * via the @read_func function. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file or %NULL if the data read is not a valid PNG image or + * memory could not be allocated for the operation. + **/ +cairo_surface_t * +cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, + void *closure) +{ + struct png_read_closure_t png_closure; + + png_closure.read_func = read_func; + png_closure.closure = closure; + + return read_png (stream_read_func, &closure); +} + diff --git a/src/cairo-private.h b/src/cairo-private.h new file mode 100644 index 000000000..a43c8ef7f --- /dev/null +++ b/src/cairo-private.h @@ -0,0 +1,51 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#ifndef CAIRO_PRIVATE_H +#define CAIRO_PRIVATE_H + +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" + +struct _cairo { + unsigned int ref_count; + + cairo_gstate_t *gstate; + cairo_path_fixed_t path; + + cairo_status_t status; +}; + +#endif /* CAIRO_PRIVATE_H */ diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 4a45fc679..a79703e1d 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -42,74 +42,34 @@ static const cairo_surface_backend_t cairo_ps_surface_backend; -/** - * cairo_set_target_ps: - * @cr: a #cairo_t - * @file: an open, writeable file - * @width_inches: width of the output page, in inches - * @height_inches: height of the output page, in inches - * @x_pixels_per_inch: X resolution to use for image fallbacks; - * not all Cairo drawing can be represented in a postscript - * file, so Cairo will write out images for some portions - * of the output. - * @y_pixels_per_inch: Y resolution to use for image fallbacks. - * - * Directs output for a #cairo_t to a postscript file. The file must - * be kept open until the #cairo_t is destroyed or set to have a - * different target, and then must be closed by the application. - **/ -void -cairo_set_target_ps (cairo_t *cr, - FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_surface_t *surface; - - surface = cairo_ps_surface_create (file, - width_inches, height_inches, - x_pixels_per_inch, y_pixels_per_inch); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - typedef struct cairo_ps_surface { cairo_surface_t base; /* PS-specific fields */ - FILE *file; + cairo_output_stream_t *stream; - double width_inches; - double height_inches; - double x_ppi; - double y_ppi; + double width_in_points; + double height_in_points; + double x_dpi; + double y_dpi; int pages; cairo_image_surface_t *image; } cairo_ps_surface_t; +#define PS_SURFACE_DPI_DEFAULT 300.0 + static void _cairo_ps_surface_erase (cairo_ps_surface_t *surface); -cairo_surface_t * -cairo_ps_surface_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) +static cairo_surface_t * +_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width_in_points, + double height_in_points) { cairo_ps_surface_t *surface; - int width, height; + int width_in_pixels, height_in_pixels; time_t now = time (0); surface = malloc (sizeof (cairo_ps_surface_t)); @@ -118,20 +78,21 @@ cairo_ps_surface_create (FILE *file, _cairo_surface_init (&surface->base, &cairo_ps_surface_backend); - surface->file = file; + surface->stream = stream; - surface->width_inches = width_inches; - surface->height_inches = height_inches; - surface->x_ppi = x_pixels_per_inch; - surface->y_ppi = x_pixels_per_inch; + surface->width_in_points = width_in_points; + surface->height_in_points = height_in_points; + surface->x_dpi = PS_SURFACE_DPI_DEFAULT; + surface->y_dpi = PS_SURFACE_DPI_DEFAULT; surface->pages = 0; - width = (int) (x_pixels_per_inch * width_inches + 1.0); - height = (int) (y_pixels_per_inch * height_inches + 1.0); + width_in_pixels = (int) (surface->x_dpi * width_in_points / 72.0 + 1.0); + height_in_pixels = (int) (surface->y_dpi * height_in_points / 72.0 + 1.0); surface->image = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width_in_pixels, height_in_pixels); if (surface->image == NULL) { free (surface); return NULL; @@ -140,26 +101,61 @@ cairo_ps_surface_create (FILE *file, _cairo_ps_surface_erase (surface); /* Document header */ - fprintf (file, - "%%!PS-Adobe-3.0\n" - "%%%%Creator: Cairo (http://cairographics.org)\n"); - fprintf (file, - "%%%%CreationDate: %s", - ctime (&now)); - fprintf (file, - "%%%%BoundingBox: %d %d %d %d\n", - 0, 0, (int) (surface->width_inches * 72.0), (int) (surface->height_inches * 72.0)); + _cairo_output_stream_printf (surface->stream, + "%%!PS-Adobe-3.0\n" + "%%%%Creator: Cairo (http://cairographics.org)\n"); + _cairo_output_stream_printf (surface->stream, + "%%%%CreationDate: %s", + ctime (&now)); + _cairo_output_stream_printf (surface->stream, + "%%%%BoundingBox: %f %f %f %f\n", + 0.0, 0.0, + surface->width_in_points, + surface->height_in_points); /* The "/FlateDecode filter" currently used is a feature of LanguageLevel 3 */ - fprintf (file, - "%%%%DocumentData: Clean7Bit\n" - "%%%%LanguageLevel: 3\n"); - fprintf (file, - "%%%%Orientation: Portrait\n" - "%%%%EndComments\n"); + _cairo_output_stream_printf (surface->stream, + "%%%%DocumentData: Clean7Bit\n" + "%%%%LanguageLevel: 3\n"); + _cairo_output_stream_printf (surface->stream, + "%%%%Orientation: Portrait\n" + "%%%%EndComments\n"); return &surface->base; } +cairo_surface_t * +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_file (filename); + if (stream == NULL) + return NULL; + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +cairo_surface_t * +cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, closure); + if (stream == NULL) + return NULL; + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + static cairo_surface_t * _cairo_ps_surface_create_similar (void *abstract_src, cairo_format_t format, @@ -170,44 +166,31 @@ _cairo_ps_surface_create_similar (void *abstract_src, return NULL; } -static void -_cairo_ps_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_ps_surface_finish (void *abstract_surface) { cairo_ps_surface_t *surface = abstract_surface; /* Document footer */ - fprintf (surface->file, "%%%%EOF\n"); + _cairo_output_stream_printf (surface->stream, + "%%%%EOF\n"); cairo_surface_destroy (&surface->image->base); - free (surface); + return CAIRO_STATUS_SUCCESS; } static void _cairo_ps_surface_erase (cairo_ps_surface_t *surface) { - cairo_color_t transparent; - - _cairo_color_init (&transparent); - _cairo_color_set_rgb (&transparent, 0., 0., 0.); - _cairo_color_set_alpha (&transparent, 0.); _cairo_surface_fill_rectangle (&surface->image->base, - CAIRO_OPERATOR_SRC, - &transparent, + CAIRO_OPERATOR_SOURCE, + CAIRO_COLOR_TRANSPARENT, 0, 0, surface->image->width, surface->image->height); } -/* XXX: We should re-work this interface to return both X/Y ppi values. */ -static double -_cairo_ps_surface_pixels_per_inch (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - - return surface->y_ppi; -} - static cairo_status_t _cairo_ps_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, @@ -220,13 +203,6 @@ _cairo_ps_surface_acquire_source_image (void *abstract_surfac return CAIRO_STATUS_SUCCESS; } -static void -_cairo_ps_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - static cairo_status_t _cairo_ps_surface_acquire_dest_image (void *abstract_surface, cairo_rectangle_t *interest_rect, @@ -246,66 +222,6 @@ _cairo_ps_surface_acquire_dest_image (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static void -_cairo_ps_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_ps_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src, - cairo_pattern_t *mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *generic_src, - void *abstract_dst, - 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) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static cairo_int_status_t _cairo_ps_surface_copy_page (void *abstract_surface) { @@ -313,13 +229,13 @@ _cairo_ps_surface_copy_page (void *abstract_surface) cairo_ps_surface_t *surface = abstract_surface; int width = surface->image->width; int height = surface->image->height; - FILE *file = surface->file; + cairo_output_stream_t *stream = surface->stream; int i, x, y; cairo_solid_pattern_t white_pattern; - char *rgb, *compressed; - long rgb_size, compressed_size; + unsigned char *rgb, *compressed; + unsigned long rgb_size, compressed_size; rgb_size = 3 * width * height; rgb = malloc (rgb_size); @@ -338,9 +254,9 @@ _cairo_ps_surface_copy_page (void *abstract_surface) /* PostScript can not represent the alpha channel, so we blend the current image over a white RGB surface to eliminate it. */ - _cairo_pattern_init_solid (&white_pattern, 1.0, 1.0, 1.0); + _cairo_pattern_init_solid (&white_pattern, CAIRO_COLOR_WHITE); - _cairo_surface_composite (CAIRO_OPERATOR_OVER_REVERSE, + _cairo_surface_composite (CAIRO_OPERATOR_DEST_OVER, &white_pattern.base, NULL, &surface->image->base, @@ -364,34 +280,34 @@ _cairo_ps_surface_copy_page (void *abstract_surface) compress (compressed, &compressed_size, rgb, rgb_size); /* Page header */ - fprintf (file, "%%%%Page: %d\n", ++surface->pages); + _cairo_output_stream_printf (stream, "%%%%Page: %d\n", ++surface->pages); - fprintf (file, "gsave\n"); + _cairo_output_stream_printf (stream, "gsave\n"); /* Image header goop */ - fprintf (file, "%g %g translate\n", 0.0, surface->height_inches * 72.0); - fprintf (file, "%g %g scale\n", 72.0 / surface->x_ppi, 72.0 / surface->y_ppi); - fprintf (file, "/DeviceRGB setcolorspace\n"); - fprintf (file, "<<\n"); - fprintf (file, " /ImageType 1\n"); - fprintf (file, " /Width %d\n", width); - fprintf (file, " /Height %d\n", height); - fprintf (file, " /BitsPerComponent 8\n"); - fprintf (file, " /Decode [ 0 1 0 1 0 1 ]\n"); - fprintf (file, " /DataSource currentfile /FlateDecode filter\n"); - fprintf (file, " /ImageMatrix [ 1 0 0 -1 0 1 ]\n"); - fprintf (file, ">>\n"); - fprintf (file, "image\n"); + _cairo_output_stream_printf (stream, "%f %f translate\n", + 0.0, surface->height_in_points); + _cairo_output_stream_printf (stream, "/DeviceRGB setcolorspace\n"); + _cairo_output_stream_printf (stream, "<<\n"); + _cairo_output_stream_printf (stream, " /ImageType 1\n"); + _cairo_output_stream_printf (stream, " /Width %d\n", width); + _cairo_output_stream_printf (stream, " /Height %d\n", height); + _cairo_output_stream_printf (stream, " /BitsPerComponent 8\n"); + _cairo_output_stream_printf (stream, " /Decode [ 0 1 0 1 0 1 ]\n"); + _cairo_output_stream_printf (stream, " /DataSource currentfile /FlateDecode filter\n"); + _cairo_output_stream_printf (stream, " /ImageMatrix [ 1 0 0 -1 0 1 ]\n"); + _cairo_output_stream_printf (stream, ">>\n"); + _cairo_output_stream_printf (stream, "image\n"); /* Compressed image data */ - fwrite (compressed, 1, compressed_size, file); + _cairo_output_stream_write (stream, compressed, compressed_size); - fprintf (file, "showpage\n"); + _cairo_output_stream_printf (stream, "showpage\n"); - fprintf (file, "grestore\n"); + _cairo_output_stream_printf (stream, "grestore\n"); /* Page footer */ - fprintf (file, "%%%%EndPage\n"); + _cairo_output_stream_printf (stream, "%%%%EndPage\n"); free (compressed); BAIL1: @@ -424,20 +340,39 @@ _cairo_ps_surface_set_clip_region (void *abstract_surface, return _cairo_image_surface_set_clip_region (surface->image, region); } +static cairo_int_status_t +_cairo_ps_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + cairo_ps_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + /* XXX: The conversion to integers here is pretty bogus, (not to + * mention the aribitray limitation of width to a short(!). We + * may need to come up with a better interface for get_size. + */ + rectangle->width = (surface->width_in_points + 0.5); + rectangle->height = (surface->height_in_points + 0.5); + + return CAIRO_STATUS_SUCCESS; +} + static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_create_similar, - _cairo_ps_surface_destroy, - _cairo_ps_surface_pixels_per_inch, + _cairo_ps_surface_finish, _cairo_ps_surface_acquire_source_image, - _cairo_ps_surface_release_source_image, + NULL, /* release_source_image */ _cairo_ps_surface_acquire_dest_image, - _cairo_ps_surface_release_dest_image, - _cairo_ps_surface_clone_similar, - _cairo_ps_surface_composite, - _cairo_ps_surface_fill_rectangles, - _cairo_ps_surface_composite_trapezoids, + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ _cairo_ps_surface_copy_page, _cairo_ps_surface_show_page, _cairo_ps_surface_set_clip_region, + _cairo_ps_surface_get_extents, NULL /* show_glyphs */ }; diff --git a/src/cairo-ps.h b/src/cairo-ps.h index 88382920e..ea2d53d09 100644 --- a/src/cairo-ps.h +++ b/src/cairo-ps.h @@ -39,30 +39,34 @@ #include <cairo.h> -#ifdef CAIRO_HAS_PS_SURFACE +#if CAIRO_HAS_PS_SURFACE #include <stdio.h> CAIRO_BEGIN_DECLS -void -cairo_set_target_ps (cairo_t *cr, - FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch); - /* PS-surface functions */ cairo_surface_t * -cairo_ps_surface_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch); +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points); + +cairo_surface_t * +cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +void +cairo_ps_surface_set_dpi (cairo_surface_t *surface, + double x_dpi, + double y_dpi); CAIRO_END_DECLS +#else /* CAIRO_HAS_PS_SURFACE */ +# error Cairo was not compiled with support for the ps backend #endif /* CAIRO_HAS_PS_SURFACE */ + #endif /* CAIRO_PS_H */ diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 01b345cdc..292f5b491 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -34,359 +34,224 @@ */ #include "cairoint.h" +#include "cairo-private.h" #include "cairo-quartz.h" -#pragma mark Types - typedef struct cairo_quartz_surface { - cairo_surface_t base; - - CGContextRef context; - - int width; - int height; - - cairo_image_surface_t *image; - - CGImageRef cgImage; -} cairo_quartz_surface_t; - - - - + cairo_surface_t base; -#pragma mark Private functions + CGContextRef context; + int width; + int height; + cairo_image_surface_t *image; + CGImageRef cgImage; +} cairo_quartz_surface_t; -void ImageDataReleaseFunc(void *info, const void *data, size_t size) -{ - if (data != NULL) - { - free((void *)data); - } -} - - - - -#pragma mark Public functions - - - - - -void -cairo_set_target_quartz_context( cairo_t *cr, - CGContextRef context, - int width, - int height) +static void +ImageDataReleaseFunc(void *info, const void *data, size_t size) { - cairo_surface_t *surface; - - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_quartz_surface_create(context, width, height); - if (surface == NULL) - { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; + if (data != NULL) { + free((void *) data); } - - cairo_set_target_surface(cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy(surface); } - -static cairo_surface_t * -_cairo_quartz_surface_create_similar( void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) +static cairo_surface_t *_cairo_quartz_surface_create_similar(void + *abstract_src, + cairo_format_t + format, + int drawable, + int width, + int height) { return NULL; } - -static void -_cairo_quartz_surface_destroy(void *abstract_surface) +static cairo_status_t +_cairo_quartz_surface_finish(void *abstract_surface) { cairo_quartz_surface_t *surface = abstract_surface; - - - if (surface->cgImage) - { - CGImageRelease(surface->cgImage); - } - - free(surface); -} + if (surface->image) + cairo_surface_destroy(&surface->image->base); + if (surface->cgImage) + CGImageRelease(surface->cgImage); -static double -_cairo_quartz_surface_pixels_per_inch(void *abstract_surface) -{ - - - // TODO - get this from CGDirectDisplay somehow? - return 96.0; + return CAIRO_STATUS_SUCCESS; } - -static cairo_image_surface_t * -_cairo_quartz_surface_get_image(void *abstract_surface) +static cairo_status_t +_cairo_quartz_surface_acquire_source_image(void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - cairo_quartz_surface_t *surface = abstract_surface; - CGColorSpaceRef colorSpace; - void *imageData; - UInt32 imageDataSize, rowBytes; - CGDataProviderRef dataProvider; - - + cairo_quartz_surface_t *surface = abstract_surface; + CGColorSpaceRef colorSpace; + void *imageData; + UInt32 imageDataSize, rowBytes; + CGDataProviderRef dataProvider; + // We keep a cached (cairo_image_surface_t *) in the cairo_quartz_surface_t // struct. If the window is ever drawn to without going through Cairo, then // we would need to refetch the pixel data from the window into the cached // image surface. - if (surface->image) - { + if (surface->image) { cairo_surface_reference(&surface->image->base); - return surface->image; + *image_out = surface->image; + return CAIRO_STATUS_SUCCESS; } - + colorSpace = CGColorSpaceCreateDeviceRGB(); - - + + rowBytes = surface->width * 4; imageDataSize = rowBytes * surface->height; imageData = malloc(imageDataSize); - - dataProvider = CGDataProviderCreateWithData(NULL, imageData, imageDataSize, ImageDataReleaseFunc); - - surface->cgImage = CGImageCreate( surface->width, - surface->height, - 8, - 32, - rowBytes, - colorSpace, - kCGImageAlphaPremultipliedFirst, - dataProvider, - NULL, - false, - kCGRenderingIntentDefault); - - + + dataProvider = + CGDataProviderCreateWithData(NULL, imageData, imageDataSize, + ImageDataReleaseFunc); + + surface->cgImage = CGImageCreate(surface->width, + surface->height, + 8, + 32, + rowBytes, + colorSpace, + kCGImageAlphaPremultipliedFirst, + dataProvider, + NULL, + false, kCGRenderingIntentDefault); + CGColorSpaceRelease(colorSpace); CGDataProviderRelease(dataProvider); - - + surface->image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data( imageData, - CAIRO_FORMAT_ARGB32, - surface->width, - surface->height, - rowBytes); - - + cairo_image_surface_create_for_data(imageData, + CAIRO_FORMAT_ARGB32, + surface->width, + surface->height, rowBytes); + + // Set the image surface Cairo state to match our own. _cairo_image_surface_set_repeat(surface->image, surface->base.repeat); - _cairo_image_surface_set_matrix(surface->image, &(surface->base.matrix)); - - - return surface->image; -} + _cairo_image_surface_set_matrix(surface->image, + &(surface->base.matrix)); + *image_out = surface->image; + *image_extra = NULL; -static cairo_status_t -_cairo_quartz_surface_set_image( void *abstract_surface, - cairo_image_surface_t *image) -{ - cairo_quartz_surface_t *surface = abstract_surface; - cairo_status_t status; - - - if (surface->image == image) - { - CGRect rect; - - - rect = CGRectMake(0, 0, surface->width, surface->height); - - CGContextDrawImage(surface->context, rect, surface->cgImage); - - - status = CAIRO_STATUS_SUCCESS; - } - else - { - // TODO - set_image from something other than what we returned from get_image - status = CAIRO_STATUS_NO_TARGET_SURFACE; - } - - - return status; + return CAIRO_STATUS_SUCCESS; } - static cairo_status_t -_cairo_quartz_surface_set_matrix(void *abstract_surface, cairo_matrix_t *matrix) +_cairo_quartz_surface_acquire_dest_image(void *abstract_surface, + cairo_rectangle_t * interest_rect, + cairo_image_surface_t ** + image_out, + cairo_rectangle_t * image_rect, + void **image_extra) { cairo_quartz_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_matrix(surface->image, matrix); -} - + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = surface->image->width; + image_rect->height = surface->image->height; -static cairo_status_t -_cairo_quartz_surface_set_filter(void *abstract_surface, cairo_filter_t filter) -{ - cairo_quartz_surface_t *surface = abstract_surface; + *image_out = surface->image; - return _cairo_image_surface_set_filter(surface->image, filter); + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_quartz_surface_set_repeat(void *abstract_surface, int repeat) +static void +_cairo_quartz_surface_release_dest_image(void *abstract_surface, + cairo_rectangle_t * + intersect_rect, + cairo_image_surface_t * image, + cairo_rectangle_t * image_rect, + void *image_extra) { cairo_quartz_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_repeat(surface->image, repeat); -} - + if (surface->image == image) { + CGRect rect; -static cairo_int_status_t -_cairo_quartz_surface_composite( cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_quartz_surface_fill_rectangles( void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} + rect = CGRectMake(0, 0, surface->width, surface->height); + CGContextDrawImage(surface->context, rect, surface->cgImage); -static cairo_int_status_t -_cairo_quartz_surface_composite_trapezoids( cairo_operator_t operator, - cairo_surface_t *generic_src, - void *abstract_dst, - int xSrc, - int ySrc, - cairo_trapezoid_t *traps, - int num_traps) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; + memset(surface->image->data, 0, surface->width * surface->height * 4); + } } - static cairo_int_status_t -_cairo_quartz_surface_copy_page(void *abstract_surface) +_cairo_quartz_surface_set_clip_region(void *abstract_surface, + pixman_region16_t * region) { - return CAIRO_INT_STATUS_UNSUPPORTED; -} - + cairo_quartz_surface_t *surface = abstract_surface; -static cairo_int_status_t -_cairo_quartz_surface_show_page(void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; + return _cairo_surface_set_clip_region(&surface->image->base, region); } - static cairo_int_status_t -_cairo_quartz_surface_set_clip_region( void *abstract_surface, - pixman_region16_t *region) +_cairo_quartz_surface_get_extents (void *abstract_surface, + cairo_rectangle_t * rectangle) { cairo_quartz_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_clip_region(surface->image, region); -} - + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; -static cairo_int_status_t -_cairo_quartz_surface_create_pattern( void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; } - static const struct _cairo_surface_backend cairo_quartz_surface_backend = { _cairo_quartz_surface_create_similar, - _cairo_quartz_surface_destroy, - _cairo_quartz_surface_pixels_per_inch, - _cairo_quartz_surface_get_image, - _cairo_quartz_surface_set_image, - _cairo_quartz_surface_set_matrix, - _cairo_quartz_surface_set_filter, - _cairo_quartz_surface_set_repeat, - _cairo_quartz_surface_composite, - _cairo_quartz_surface_fill_rectangles, - _cairo_quartz_surface_composite_trapezoids, - _cairo_quartz_surface_copy_page, - _cairo_quartz_surface_show_page, + _cairo_quartz_surface_finish, + _cairo_quartz_surface_acquire_source_image, + NULL, /* release_source_image */ + _cairo_quartz_surface_acquire_dest_image, + _cairo_quartz_surface_release_dest_image, + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* copy_page */ + NULL, /* show_page */ _cairo_quartz_surface_set_clip_region, - _cairo_quartz_surface_create_pattern + _cairo_quartz_surface_get_extents, + NULL /* show_glyphs */ }; -cairo_surface_t * -cairo_quartz_surface_create( CGContextRef context, - int width, - int height) +cairo_surface_t *cairo_quartz_surface_create(CGContextRef context, + int width, int height) { cairo_quartz_surface_t *surface; - - + surface = malloc(sizeof(cairo_quartz_surface_t)); if (surface == NULL) return NULL; - + _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend); - - - surface->context = context; - - surface->width = width; - surface->height = height; - - surface->image = NULL; - - surface->cgImage = NULL; - - - // Set up the image surface which Cairo draws into and we blit to & from. - surface->image = _cairo_quartz_surface_get_image(surface); - - - return (cairo_surface_t *)surface; -} + surface->context = context; + surface->width = width; + surface->height = height; + surface->image = NULL; + surface->cgImage = NULL; -DEPRECATE (cairo_surface_create_for_drawable, cairo_quartz_surface_create); + // Set up the image surface which Cairo draws into and we blit to & from. + void *foo; + _cairo_quartz_surface_acquire_source_image(surface, &surface->image, &foo); + + return (cairo_surface_t *) surface; +} diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h index 5afd46426..6f59f6a79 100644 --- a/src/cairo-quartz.h +++ b/src/cairo-quartz.h @@ -39,18 +39,12 @@ #include <cairo.h> -#ifdef CAIRO_HAS_QUARTZ_SURFACE +#if CAIRO_HAS_QUARTZ_SURFACE #include <Carbon/Carbon.h> CAIRO_BEGIN_DECLS -void -cairo_set_target_quartz_context( cairo_t *cr, - CGContextRef context, - int width, - int height); - cairo_surface_t * cairo_quartz_surface_create ( CGContextRef context, int width, @@ -58,6 +52,9 @@ cairo_quartz_surface_create ( CGContextRef context, CAIRO_END_DECLS +#else /* CAIRO_HAS_QUARTZ_SURFACE */ +# error Cairo was not compiled with support for the quartz backend #endif /* CAIRO_HAS_QUARTZ_SURFACE */ + #endif /* CAIRO_QUARTZ_H */ diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 330d58b1e..0bcf80cf8 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -38,6 +39,17 @@ #include "cairoint.h" +struct _cairo_surface_save { + cairo_surface_save_t *next; + pixman_region16_t *clip_region; +}; + +static cairo_status_t +_cairo_surface_set_clip_region_internal (cairo_surface_t *surface, + pixman_region16_t *region, + cairo_bool_t copy_region, + cairo_bool_t free_existing); + void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend) @@ -45,22 +57,122 @@ _cairo_surface_init (cairo_surface_t *surface, surface->backend = backend; surface->ref_count = 1; + surface->finished = FALSE; - _cairo_matrix_init (&surface->matrix); - surface->filter = CAIRO_FILTER_NEAREST; + _cairo_user_data_array_init (&surface->user_data); + + cairo_matrix_init_identity (&surface->matrix); + surface->filter = CAIRO_FILTER_GOOD; surface->repeat = 0; + + surface->device_x_offset = 0; + surface->device_y_offset = 0; + + surface->clip_region = NULL; + + surface->saves = NULL; + surface->level = 0; } -cairo_surface_t * -cairo_surface_create_for_image (char *data, - cairo_format_t format, - int width, - int height, - int stride) +static cairo_status_t +_cairo_surface_begin_internal (cairo_surface_t *surface, + cairo_bool_t reset_clip) +{ + cairo_surface_save_t *save; + + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + save = malloc (sizeof (cairo_surface_save_t)); + if (!save) + return CAIRO_STATUS_NO_MEMORY; + + if (surface->clip_region) { + if (reset_clip) + { + cairo_status_t status; + + save->clip_region = surface->clip_region; + status = _cairo_surface_set_clip_region_internal (surface, NULL, FALSE, FALSE); + if (!CAIRO_OK (status)) { + free (save); + return status; + } + } + else + { + save->clip_region = pixman_region_create (); + pixman_region_copy (save->clip_region, surface->clip_region); + } + } else { + save->clip_region = NULL; + } + + save->next = surface->saves; + surface->saves = save; + surface->level++; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_surface_begin: + * @surface: a #cairo_surface_t + * + * Must be called before beginning to use the surface. State + * of the surface like the clip region will be saved then restored + * on the matching call _cairo_surface_end(). + */ +cairo_private cairo_status_t +_cairo_surface_begin (cairo_surface_t *surface) +{ + return _cairo_surface_begin_internal (surface, FALSE); +} + +/** + * _cairo_surface_begin_reset_clip: + * @surface: a #cairo_surface_t + * + * Must be called before beginning to use the surface. State + * of the surface like the clip region will be saved then restored + * on the matching call _cairo_surface_end(). + * + * After the state is saved, the clip region is cleared. This + * combination of operations is a little artificial; the caller could + * simply call _cairo_surface_set_clip_region (surface, NULL); after + * _cairo_surface_save(). Combining the two saves a copy of the clip + * region, and also simplifies error handling for the caller. + **/ +cairo_private cairo_status_t +_cairo_surface_begin_reset_clip (cairo_surface_t *surface) +{ + return _cairo_surface_begin_internal (surface, TRUE); +} + +/** + * _cairo_surface_end: + * @surface: a #cairo_surface_t + * + * Restores any state saved by _cairo_surface_begin() + **/ +cairo_private cairo_status_t +_cairo_surface_end (cairo_surface_t *surface) { - return cairo_image_surface_create_for_data (data, format, width, height, stride); + cairo_surface_save_t *save; + pixman_region16_t *clip_region; + + if (!surface->saves) + return CAIRO_STATUS_BAD_NESTING; + + save = surface->saves; + surface->saves = save->next; + surface->level--; + + clip_region = save->clip_region; + free (save); + + return _cairo_surface_set_clip_region_internal (surface, clip_region, FALSE, TRUE); } -slim_hidden_def(cairo_surface_create_for_image); cairo_surface_t * _cairo_surface_create_similar_scratch (cairo_surface_t *other, @@ -82,24 +194,20 @@ cairo_surface_create_similar (cairo_surface_t *other, int width, int height) { - cairo_color_t empty; - if (other == NULL) return NULL; - _cairo_color_init (&empty); - _cairo_color_set_rgb (&empty, 0., 0., 0.); - _cairo_color_set_alpha (&empty, 0.); - - return _cairo_surface_create_similar_solid (other, format, width, height, &empty); + return _cairo_surface_create_similar_solid (other, format, + width, height, + CAIRO_COLOR_TRANSPARENT); } cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_format_t format, - int width, - int height, - cairo_color_t *color) +_cairo_surface_create_similar_solid (cairo_surface_t *other, + cairo_format_t format, + int width, + int height, + const cairo_color_t *color) { cairo_status_t status; cairo_surface_t *surface; @@ -111,7 +219,7 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, surface = cairo_image_surface_create (format, width, height); status = _cairo_surface_fill_rectangle (surface, - CAIRO_OPERATOR_SRC, color, + CAIRO_OPERATOR_SOURCE, color, 0, 0, width, height); if (status) { cairo_surface_destroy (surface); @@ -140,15 +248,134 @@ cairo_surface_destroy (cairo_surface_t *surface) if (surface->ref_count) return; - if (surface->backend->destroy) - surface->backend->destroy (surface); + cairo_surface_finish (surface); + + _cairo_user_data_array_destroy (&surface->user_data); + + free (surface); } slim_hidden_def(cairo_surface_destroy); -double -_cairo_surface_pixels_per_inch (cairo_surface_t *surface) +/** + * cairo_surface_finish: + * @surface: the #cairo_surface_t to finish + * + * This function finishes the surface and drops all references to + * external resources. For example, for the Xlib backend it means + * that cairo will no longer access the drawable, which can be freed. + * After calling cairo_surface_finish() the only valid operations on a + * surface are getting and setting user data and referencing and + * destroying it. Further drawing the the surface will not affect the + * surface but set the surface status to + * CAIRO_STATUS_SURFACE_FINISHED. + * + * When the last call to cairo_surface_destroy() decreases the + * reference count to zero, cairo will call cairo_surface_finish() if + * it hasn't been called already, before freeing the resources + * associated with the surface. + * + * Return value: CAIRO_STATUS_SUCCESS if the surface was finished + * successfully, otherwise CAIRO_STATUS_NO_MEMORY or + * CAIRO_STATUS_WRITE_ERROR. + **/ +cairo_status_t +cairo_surface_finish (cairo_surface_t *surface) +{ + cairo_status_t status; + + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (surface->saves) + return CAIRO_STATUS_BAD_NESTING; + + if (surface->clip_region) { + pixman_region_destroy (surface->clip_region); + surface->clip_region = NULL; + } + + if (surface->backend->finish) { + status = surface->backend->finish (surface); + if (status) + return status; + } + + surface->finished = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_surface_get_user_data: + * @surface: a #cairo_surface_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @surface using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +cairo_surface_get_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key) { - return surface->backend->pixels_per_inch (surface); + return _cairo_user_data_array_get_data (&surface->user_data, + key); +} + +/** + * cairo_surface_set_user_data: + * @surface: a #cairo_surface_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the surface + * @destroy: a #cairo_destroy_func_t which will be called when the + * surface is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @surface. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +cairo_surface_set_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + return _cairo_user_data_array_set_data (&surface->user_data, + key, user_data, destroy); +} + +/** + * cairo_surface_set_device_offset: + * @surface: a #cairo_surface_t + * @x_offset: the offset in the X direction, in device units + * @y_offset: the offset in the Y direction, in device units + * + * Sets an offset that is added to the device coordinates determined + * by the CTM when drawing to @surface. One use case for this function + * is when we want to create a #cairo_surface_t that redirects drawing + * for a portion of an onscreen surface to an offscreen surface in a + * way that is completely invisible to the user of the cairo + * API. Setting a transformation via cairo_translate() isn't + * sufficient to do this, since functions like + * cairo_device_to_user() will expose the hidden offset. + * + * Note that the offset only affects drawing to the surface, not using + * the surface in a surface pattern. + **/ +void +cairo_surface_set_device_offset (cairo_surface_t *surface, + double x_offset, + double y_offset) +{ + surface->device_x_offset = x_offset; + surface->device_y_offset = y_offset; } /** @@ -173,6 +400,8 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, void **image_extra) { + assert (!surface->finished); + return surface->backend->acquire_source_image (surface, image_out, image_extra); } @@ -188,7 +417,10 @@ _cairo_surface_release_source_image (cairo_surface_t *surface, cairo_image_surface_t *image, void *image_extra) { - surface->backend->release_source_image (surface, image, image_extra); + assert (!surface->finished); + + if (surface->backend->release_source_image) + surface->backend->release_source_image (surface, image, image_extra); } /** @@ -226,6 +458,8 @@ _cairo_surface_acquire_dest_image (cairo_surface_t *surface, cairo_rectangle_t *image_rect, void **image_extra) { + assert (!surface->finished); + return surface->backend->acquire_dest_image (surface, interest_rect, image_out, image_rect, image_extra); } @@ -249,8 +483,11 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, cairo_rectangle_t *image_rect, void *image_extra) { - surface->backend->release_dest_image (surface, interest_rect, - image, image_rect, image_extra); + assert (!surface->finished); + + if (surface->backend->release_dest_image) + surface->backend->release_dest_image (surface, interest_rect, + image, image_rect, image_extra); } /** @@ -278,9 +515,14 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_image_surface_t *image; void *image_extra; - status = surface->backend->clone_similar (surface, src, clone_out); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (surface->backend->clone_similar) { + status = surface->backend->clone_similar (surface, src, clone_out); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } status = _cairo_surface_acquire_source_image (src, &image, &image_extra); if (status != CAIRO_STATUS_SUCCESS) @@ -298,69 +540,6 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, return status; } -cairo_status_t -cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - return cairo_matrix_copy (&surface->matrix, matrix); -} -slim_hidden_def(cairo_surface_set_matrix); - -cairo_status_t -cairo_surface_get_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - return cairo_matrix_copy (matrix, &surface->matrix); -} -slim_hidden_def(cairo_surface_get_matrix); - -cairo_status_t -cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - surface->filter = filter; - return CAIRO_STATUS_SUCCESS; -} - -cairo_filter_t -cairo_surface_get_filter (cairo_surface_t *surface) -{ - return surface->filter; -} - -/* XXX: NYI -cairo_status_t -cairo_surface_clip_rectangle (cairo_surface_t *surface, - int x, int y, - int width, int height) -{ - -} -*/ - -/* XXX: NYI -cairo_status_t -cairo_surface_clip_restore (cairo_surface_t *surface); -*/ - -cairo_status_t -cairo_surface_set_repeat (cairo_surface_t *surface, int repeat) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - surface->repeat = repeat; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_surface_set_repeat); - typedef struct { cairo_surface_t *dst; cairo_rectangle_t extents; @@ -444,14 +623,19 @@ _cairo_surface_composite (cairo_operator_t operator, { cairo_int_status_t status; - status = dst->backend->composite (operator, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + if (dst->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (dst->backend->composite) { + status = dst->backend->composite (operator, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } return _fallback_composite (operator, src, mask, dst, @@ -462,16 +646,19 @@ _cairo_surface_composite (cairo_operator_t operator, } cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t operator, - cairo_color_t *color, - int x, - int y, - int width, - int height) +_cairo_surface_fill_rectangle (cairo_surface_t *surface, + cairo_operator_t operator, + const cairo_color_t *color, + int x, + int y, + int width, + int height) { cairo_rectangle_t rect; + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + rect.x = x; rect.y = y; rect.width = width; @@ -505,15 +692,15 @@ _fallback_fill_rectangles (cairo_surface_t *surface, y2 = rects[0].y + rects[0].height; for (i = 1; i < num_rects; i++) { - if (rects[0].x < x1) - x1 = rects[0].x; - if (rects[0].y < y1) - y1 = rects[0].y; - - if (rects[0].x + rects[0].width > x2) - x2 = rects[0].x + rects[0].width; - if (rects[0].y + rects[0].height > y2) - y2 = rects[0].y + rects[0].height; + if (rects[i].x < x1) + x1 = rects[i].x; + if (rects[i].y < y1) + y1 = rects[i].y; + + if (rects[i].x + rects[i].width > x2) + x2 = rects[i].x + rects[i].width; + if (rects[i].y + rects[i].height > y2) + y2 = rects[i].y + rects[i].height; } status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); @@ -560,19 +747,37 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, { cairo_int_status_t status; + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + if (num_rects == 0) return CAIRO_STATUS_SUCCESS; - status = surface->backend->fill_rectangles (surface, - operator, - color, - rects, num_rects); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + if (surface->backend->fill_rectangles) { + status = surface->backend->fill_rectangles (surface, + operator, + color, + rects, num_rects); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); } +cairo_private cairo_int_status_t +_cairo_surface_fill_path (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_path_fixed_t *path) +{ + if (dst->backend->fill_path) + return dst->backend->fill_path (operator, pattern, dst, path); + else + return CAIRO_INT_STATUS_UNSUPPORTED; +} + + static cairo_status_t _fallback_composite_trapezoids (cairo_operator_t operator, cairo_pattern_t *pattern, @@ -655,14 +860,19 @@ _cairo_surface_composite_trapezoids (cairo_operator_t operator, { cairo_int_status_t status; - status = dst->backend->composite_trapezoids (operator, - pattern, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + if (dst->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (dst->backend->composite_trapezoids) { + status = dst->backend->composite_trapezoids (operator, + pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } return _fallback_composite_trapezoids (operator, pattern, dst, src_x, src_y, @@ -674,35 +884,116 @@ _cairo_surface_composite_trapezoids (cairo_operator_t operator, cairo_status_t _cairo_surface_copy_page (cairo_surface_t *surface) { - cairo_int_status_t status; + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; - status = surface->backend->copy_page (surface); /* It's fine if some backends just don't support this. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) + if (surface->backend->copy_page == NULL) return CAIRO_STATUS_SUCCESS; - if (status) - return status; - return CAIRO_STATUS_SUCCESS; + return surface->backend->copy_page (surface); } cairo_status_t _cairo_surface_show_page (cairo_surface_t *surface) { - cairo_int_status_t status; + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; - status = surface->backend->show_page (surface); /* It's fine if some backends just don't support this. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) + if (surface->backend->show_page == NULL) return CAIRO_STATUS_SUCCESS; - if (status) - return status; - return CAIRO_STATUS_SUCCESS; + return surface->backend->show_page (surface); } -cairo_status_t -_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region) +static cairo_status_t +_cairo_surface_set_clip_region_internal (cairo_surface_t *surface, + pixman_region16_t *region, + cairo_bool_t copy_region, + cairo_bool_t free_existing) { + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (region == surface->clip_region) + return CAIRO_STATUS_SUCCESS; + + if (surface->backend->set_clip_region == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->clip_region) { + if (free_existing) + pixman_region_destroy (surface->clip_region); + surface->clip_region = NULL; + } + + if (region) { + if (copy_region) { + surface->clip_region = pixman_region_create (); + pixman_region_copy (surface->clip_region, region); + } else + surface->clip_region = region; + } + return surface->backend->set_clip_region (surface, region); } + +cairo_status_t +_cairo_surface_set_clip_region (cairo_surface_t *surface, + pixman_region16_t *region) +{ + return _cairo_surface_set_clip_region_internal (surface, region, TRUE, TRUE); +} + +cairo_status_t +_cairo_surface_get_clip_extents (cairo_surface_t *surface, + cairo_rectangle_t *rectangle) +{ + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (surface->clip_region) { + pixman_box16_t *box = pixman_region_extents (surface->clip_region); + + rectangle->x = box->x1; + rectangle->y = box->y1; + rectangle->width = box->x2 - box->x1; + rectangle->height = box->y2 - box->y1; + + return CAIRO_STATUS_SUCCESS; + } + + return surface->backend->get_extents (surface, rectangle); +} + +cairo_status_t +_cairo_surface_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *dst, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_status_t status; + + if (dst->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + + if (dst->backend->show_glyphs) + status = dst->backend->show_glyphs (scaled_font, operator, pattern, dst, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs); + else + status = CAIRO_INT_STATUS_UNSUPPORTED; + + return status; +} diff --git a/src/cairo-traps.c b/src/cairo-traps.c index 79c7e16b6..15dfc1bca 100644 --- a/src/cairo-traps.c +++ b/src/cairo-traps.c @@ -88,6 +88,43 @@ _cairo_traps_fini (cairo_traps_t *traps) } } +/** + * _cairo_traps_init_box: + * @traps: a #cairo_traps_t + * @box: a box that will be converted to a single trapezoid + * to store in @traps. + * + * Initializes a cairo_traps_t to contain a single rectangular + * trapezoid. + **/ +cairo_status_t +_cairo_traps_init_box (cairo_traps_t *traps, + cairo_box_t *box) +{ + cairo_status_t status; + + _cairo_traps_init (traps); + + status = _cairo_traps_grow_by (traps, 1); + if (status) + return status; + + traps->num_traps = 1; + + traps->traps[0].top = box->p1.y; + traps->traps[0].bottom = box->p2.y; + traps->traps[0].left.p1 = box->p1; + traps->traps[0].left.p2.x = box->p1.x; + traps->traps[0].left.p2.y = box->p2.y; + traps->traps[0].right.p1.x = box->p2.x; + traps->traps[0].right.p1.y = box->p1.y; + traps->traps[0].right.p2 = box->p2; + + traps->extents = *box; + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, cairo_line_t *left, cairo_line_t *right) @@ -738,3 +775,65 @@ _cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents) { *extents = traps->extents; } + +/** + * _cairo_traps_extract_region: + * @traps: a #cairo_traps_t + * @region: on return, %NULL is stored here if the trapezoids aren't + * exactly representable as a pixman region, otherwise a + * a pointer to such a region, newly allocated. + * (free with pixman region destroy) + * + * Determines if a set of trapezoids are exactly representable as a + * pixman region, and if so creates such a region. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + **/ +cairo_status_t +_cairo_traps_extract_region (cairo_traps_t *traps, + pixman_region16_t **region) +{ + int i; + + for (i = 0; i < traps->num_traps; i++) + if (!(traps->traps[i].left.p1.x == traps->traps[i].left.p2.x + && traps->traps[i].right.p1.x == traps->traps[i].right.p2.x + && traps->traps[i].left.p1.y == traps->traps[i].right.p1.y + && traps->traps[i].left.p2.y == traps->traps[i].right.p2.y + && _cairo_fixed_is_integer(traps->traps[i].left.p1.x) + && _cairo_fixed_is_integer(traps->traps[i].left.p1.y) + && _cairo_fixed_is_integer(traps->traps[i].left.p2.x) + && _cairo_fixed_is_integer(traps->traps[i].left.p2.y) + && _cairo_fixed_is_integer(traps->traps[i].right.p1.x) + && _cairo_fixed_is_integer(traps->traps[i].right.p1.y) + && _cairo_fixed_is_integer(traps->traps[i].right.p2.x) + && _cairo_fixed_is_integer(traps->traps[i].right.p2.y))) { + *region = NULL; + return CAIRO_STATUS_SUCCESS; + } + + *region = pixman_region_create (); + + for (i = 0; i < traps->num_traps; i++) { + int x = _cairo_fixed_integer_part(traps->traps[i].left.p1.x); + int y = _cairo_fixed_integer_part(traps->traps[i].left.p1.y); + int width = _cairo_fixed_integer_part(traps->traps[i].right.p1.x) - x; + int height = _cairo_fixed_integer_part(traps->traps[i].left.p2.y) - y; + + /* Sometimes we get degenerate trapezoids from the tesellator, + * if we call pixman_region_union_rect(), it bizarrly fails on such + * an empty rectangle, so skip them. + */ + if (width == 0 || height == 0) + continue; + + if (pixman_region_union_rect (*region, *region, + x, y, width, height) != PIXMAN_REGION_STATUS_SUCCESS) { + pixman_region_destroy (*region); + return CAIRO_STATUS_NO_MEMORY; + } + } + + return CAIRO_STATUS_SUCCESS; +} + diff --git a/src/cairo-unicode.c b/src/cairo-unicode.c index 92201391a..8929baaac 100644 --- a/src/cairo-unicode.c +++ b/src/cairo-unicode.c @@ -117,14 +117,14 @@ static const char utf8_skip_data[256] = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 }; -#define UTF8_NEXT_CHAR(p) (char *)((p) + utf8_skip_data[*(unsigned char *)(p)]) +#define UTF8_NEXT_CHAR(p) ((p) + utf8_skip_data[*(unsigned char *)(p)]) /* Converts a sequence of bytes encoded as UTF-8 to a Unicode character. * If @p does not point to a valid UTF-8 encoded character, results are * undefined. **/ static uint32_t -_utf8_get_char (const char *p) +_utf8_get_char (const unsigned char *p) { int i, mask = 0, len; uint32_t result; @@ -142,8 +142,8 @@ _utf8_get_char (const char *p) * and return (uint32_t)-2 on incomplete trailing character */ static uint32_t -_utf8_get_char_extended (const char *p, - long max_len) +_utf8_get_char_extended (const unsigned char *p, + long max_len) { int i, len; uint32_t wc = (unsigned char) *p; @@ -220,14 +220,14 @@ _utf8_get_char_extended (const char *p, * an invalid sequence was found. **/ cairo_status_t -_cairo_utf8_to_ucs4 (const char *str, - int len, - uint32_t **result, - int *items_written) +_cairo_utf8_to_ucs4 (const unsigned char *str, + int len, + uint32_t **result, + int *items_written) { uint32_t *str32 = NULL; int n_chars, i; - const char *in; + const unsigned char *in; in = str; n_chars = 0; @@ -284,14 +284,14 @@ _cairo_utf8_to_ucs4 (const char *str, * an invalid sequence was found. **/ cairo_status_t -_cairo_utf8_to_utf16 (const char *str, - int len, - uint16_t **result, - int *items_written) +_cairo_utf8_to_utf16 (const unsigned char *str, + int len, + uint16_t **result, + int *items_written) { uint16_t *str16 = NULL; int n16, i; - const char *in; + const unsigned char *in; in = str; n16 = 0; diff --git a/src/cairo-wideint.h b/src/cairo-wideint.h index abe36f9d4..3afea5a65 100644 --- a/src/cairo-wideint.h +++ b/src/cairo-wideint.h @@ -1,5 +1,5 @@ /* - * $Id: cairo-wideint.h,v 1.6 2005-01-19 15:11:14 cworth Exp $ + * $Id: cairo-wideint.h,v 1.10 2005-05-10 19:42:32 cworth Exp $ * * Copyright © 2004 Keith Packard * @@ -38,7 +38,15 @@ #ifndef CAIRO_WIDEINT_H #define CAIRO_WIDEINT_H -#include <stdint.h> +#if HAVE_STDINT_H +# include <stdint.h> +#elif HAVE_INTTYPES_H +# include <inttypes.h> +#elif HAVE_SYS_INT_TYPES_H +# include <sys/int_types.h> +#else +#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.) +#endif /* * 64-bit datatypes. Two separate implementations, one using diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c index 02f0cffd6..78b3ca212 100644 --- a/src/cairo-win32-font.c +++ b/src/cairo-win32-font.c @@ -34,7 +34,7 @@ #include <string.h> #include <stdio.h> - +#include "cairoint.h" #include "cairo-win32-private.h" #ifndef SPI_GETFONTSMOOTHINGTYPE @@ -47,12 +47,12 @@ #define CLEARTYPE_QUALITY 5 #endif -const cairo_font_backend_t cairo_win32_font_backend; +const cairo_scaled_font_backend_t cairo_win32_scaled_font_backend; #define LOGICAL_SCALE 32 typedef struct { - cairo_font_t base; + cairo_scaled_font_t base; LOGFONTW logfont; @@ -93,73 +93,69 @@ typedef struct { */ int em_square; - HFONT scaled_font; - HFONT unscaled_font; + HFONT scaled_hfont; + HFONT unscaled_hfont; -} cairo_win32_font_t; +} cairo_win32_scaled_font_t; #define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) static void -_compute_transform (cairo_win32_font_t *font, - cairo_font_scale_t *sc) +_compute_transform (cairo_win32_scaled_font_t *scaled_font, + cairo_matrix_t *sc) { - if (NEARLY_ZERO (sc->matrix[0][1]) && NEARLY_ZERO (sc->matrix[1][0])) { - font->preserve_axes = TRUE; - font->x_scale = sc->matrix[0][0]; - font->swap_x = (sc->matrix[0][0] < 0); - font->y_scale = sc->matrix[1][1]; - font->swap_y = (sc->matrix[1][1] < 0); - font->swap_axes = FALSE; + if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->xx; + scaled_font->swap_x = (sc->xx < 0); + scaled_font->y_scale = sc->yy; + scaled_font->swap_y = (sc->yy < 0); + scaled_font->swap_axes = FALSE; - } else if (NEARLY_ZERO (sc->matrix[0][0]) && NEARLY_ZERO (sc->matrix[1][1])) { - font->preserve_axes = TRUE; - font->x_scale = sc->matrix[0][1]; - font->swap_x = (sc->matrix[0][1] < 0); - font->y_scale = sc->matrix[1][0]; - font->swap_y = (sc->matrix[1][0] < 0); - font->swap_axes = TRUE; + } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->yx; + scaled_font->swap_x = (sc->yx < 0); + scaled_font->y_scale = sc->xy; + scaled_font->swap_y = (sc->xy < 0); + scaled_font->swap_axes = TRUE; } else { - font->preserve_axes = FALSE; - font->swap_x = font->swap_y = font->swap_axes = FALSE; + scaled_font->preserve_axes = FALSE; + scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE; } - if (font->preserve_axes) { - if (font->swap_x) - font->x_scale = - font->x_scale; - if (font->swap_y) - font->y_scale = - font->y_scale; + if (scaled_font->preserve_axes) { + if (scaled_font->swap_x) + scaled_font->x_scale = - scaled_font->x_scale; + if (scaled_font->swap_y) + scaled_font->y_scale = - scaled_font->y_scale; - font->logical_scale = LOGICAL_SCALE * font->y_scale; - font->logical_size = LOGICAL_SCALE * floor (font->y_scale + 0.5); + scaled_font->logical_scale = LOGICAL_SCALE * scaled_font->y_scale; + scaled_font->logical_size = LOGICAL_SCALE * floor (scaled_font->y_scale + 0.5); } /* The font matrix has x and y "scale" components which we extract and * use as character scale values. */ - cairo_matrix_set_affine (&font->logical_to_device, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - if (!font->preserve_axes) { - _cairo_matrix_compute_scale_factors (&font->logical_to_device, - &font->x_scale, &font->y_scale, + cairo_matrix_init (&scaled_font->logical_to_device, + sc->xx, sc->yx, sc->xy, sc->yy, 0, 0); + + if (!scaled_font->preserve_axes) { + _cairo_matrix_compute_scale_factors (&scaled_font->logical_to_device, + &scaled_font->x_scale, &scaled_font->y_scale, TRUE); /* XXX: Handle vertical text */ - font->logical_size = floor (LOGICAL_SCALE * font->y_scale + 0.5); - font->logical_scale = LOGICAL_SCALE * font->y_scale; + scaled_font->logical_size = floor (LOGICAL_SCALE * scaled_font->y_scale + 0.5); + scaled_font->logical_scale = LOGICAL_SCALE * scaled_font->y_scale; } - cairo_matrix_scale (&font->logical_to_device, - 1.0 / font->logical_scale, 1.0 / font->logical_scale); + cairo_matrix_scale (&scaled_font->logical_to_device, + 1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale); - font->device_to_logical = font->logical_to_device; - if (!CAIRO_OK (cairo_matrix_invert (&font->device_to_logical))) - cairo_matrix_set_identity (&font->device_to_logical); + scaled_font->device_to_logical = scaled_font->logical_to_device; + if (!CAIRO_OK (cairo_matrix_invert (&scaled_font->device_to_logical))) + cairo_matrix_init_identity (&scaled_font->device_to_logical); } static BYTE @@ -202,53 +198,56 @@ _get_system_quality (void) return DEFAULT_QUALITY; } -static cairo_font_t * -_win32_font_create (LOGFONTW *logfont, - cairo_font_scale_t *scale) +static cairo_scaled_font_t * +_win32_scaled_font_create (LOGFONTW *logfont, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm) { - cairo_win32_font_t *f; + cairo_win32_scaled_font_t *f; + cairo_matrix_t scale; - f = malloc (sizeof(cairo_win32_font_t)); + f = malloc (sizeof(cairo_win32_scaled_font_t)); if (f == NULL) return NULL; f->logfont = *logfont; f->quality = _get_system_quality (); f->em_square = 0; - f->scaled_font = NULL; - f->unscaled_font = NULL; + f->scaled_hfont = NULL; + f->unscaled_hfont = NULL; - _compute_transform (f, scale); - - _cairo_font_init ((cairo_font_t *)f, scale, &cairo_win32_font_backend); + cairo_matrix_multiply (&scale, font_matrix, ctm); + _compute_transform (f, &scale); + + _cairo_scaled_font_init (&f->base, font_matrix, ctm, &cairo_win32_scaled_font_backend); - return (cairo_font_t *)f; + return &f->base; } static cairo_status_t -_win32_font_set_world_transform (cairo_win32_font_t *font, - HDC hdc) +_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, + HDC hdc) { XFORM xform; - xform.eM11 = font->logical_to_device.m[0][0]; - xform.eM21 = font->logical_to_device.m[1][0]; - xform.eM12 = font->logical_to_device.m[0][1]; - xform.eM22 = font->logical_to_device.m[1][1]; - xform.eDx = font->logical_to_device.m[2][0]; - xform.eDy = font->logical_to_device.m[2][1]; + xform.eM11 = scaled_font->logical_to_device.xx; + xform.eM21 = scaled_font->logical_to_device.xy; + xform.eM12 = scaled_font->logical_to_device.yx; + xform.eM22 = scaled_font->logical_to_device.yy; + xform.eDx = scaled_font->logical_to_device.x0; + xform.eDy = scaled_font->logical_to_device.y0; if (!SetWorldTransform (hdc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_font_set_world_transform"); + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_win32_font_set_identity_transform (HDC hdc) +_win32_scaled_font_set_identity_transform (HDC hdc) { if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_win32_font_set_identity_transform"); + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); return CAIRO_STATUS_SUCCESS; } @@ -276,48 +275,48 @@ _get_global_font_dc (void) } static HFONT -_win32_font_get_scaled_font (cairo_win32_font_t *font) +_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font) { - if (!font->scaled_font) { - LOGFONTW logfont = font->logfont; - logfont.lfHeight = font->logical_size; + if (!scaled_font->scaled_hfont) { + LOGFONTW logfont = scaled_font->logfont; + logfont.lfHeight = scaled_font->logical_size; logfont.lfWidth = 0; logfont.lfEscapement = 0; logfont.lfOrientation = 0; - logfont.lfQuality = font->quality; + logfont.lfQuality = scaled_font->quality; - font->scaled_font = CreateFontIndirectW (&logfont); - if (!font->scaled_font) { - _cairo_win32_print_gdi_error ("_win32_font_get_scaled_font"); + scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); + if (!scaled_font->scaled_hfont) { + _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); return NULL; } } - return font->scaled_font; + return scaled_font->scaled_hfont; } static HFONT -_win32_font_get_unscaled_font (cairo_win32_font_t *font, - HDC hdc) +_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HDC hdc) { - if (!font->unscaled_font) { + if (!scaled_font->unscaled_hfont) { OUTLINETEXTMETRIC *otm; unsigned int otm_size; - HFONT scaled_font; + HFONT scaled_hfont; LOGFONTW logfont; - scaled_font = _win32_font_get_scaled_font (font); - if (!scaled_font) + scaled_hfont = _win32_scaled_font_get_scaled_hfont (scaled_font); + if (!scaled_hfont) return NULL; - if (!SelectObject (hdc, scaled_font)) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:SelectObject"); + if (!SelectObject (hdc, scaled_hfont)) { + _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); return NULL; } otm_size = GetOutlineTextMetrics (hdc, 0, NULL); if (!otm_size) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); + _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); return NULL; } @@ -326,48 +325,48 @@ _win32_font_get_unscaled_font (cairo_win32_font_t *font, return NULL; if (!GetOutlineTextMetrics (hdc, otm_size, otm)) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); + _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); free (otm); return NULL; } - font->em_square = otm->otmEMSquare; + scaled_font->em_square = otm->otmEMSquare; free (otm); - logfont = font->logfont; - logfont.lfHeight = font->em_square; + logfont = scaled_font->logfont; + logfont.lfHeight = scaled_font->em_square; logfont.lfWidth = 0; logfont.lfEscapement = 0; logfont.lfOrientation = 0; - logfont.lfQuality = font->quality; + logfont.lfQuality = scaled_font->quality; - font->unscaled_font = CreateFontIndirectW (&logfont); - if (!font->unscaled_font) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:CreateIndirect"); + scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); + if (!scaled_font->unscaled_hfont) { + _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); return NULL; } } - return font->unscaled_font; + return scaled_font->unscaled_hfont; } static cairo_status_t -_cairo_win32_font_select_unscaled_font (cairo_font_t *font, - HDC hdc) +_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, + HDC hdc) { cairo_status_t status; HFONT hfont; HFONT old_hfont = NULL; - hfont = _win32_font_get_unscaled_font ((cairo_win32_font_t *)font, hdc); + hfont = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc); if (!hfont) return CAIRO_STATUS_NO_MEMORY; old_hfont = SelectObject (hdc, hfont); if (!old_hfont) - return _cairo_win32_print_gdi_error ("_cairo_win32_font_select_unscaled_font"); + return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); - status = _win32_font_set_identity_transform (hdc); + status = _win32_scaled_font_set_identity_transform (hdc); if (!CAIRO_OK (status)) { SelectObject (hdc, old_hfont); return status; @@ -379,21 +378,22 @@ _cairo_win32_font_select_unscaled_font (cairo_font_t *font, } static void -_cairo_win32_font_done_unscaled_font (cairo_font_t *font) +_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) { } /* implement the font backend interface */ static cairo_status_t -_cairo_win32_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *scale, - cairo_font_t **font_out) +_cairo_win32_scaled_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **scaled_font_out) { LOGFONTW logfont; - cairo_font_t *font; + cairo_scaled_font_t *scaled_font; uint16_t *face_name; int face_name_len; cairo_status_t status; @@ -451,47 +451,40 @@ _cairo_win32_font_create (const char *family, if (!logfont.lfFaceName) return CAIRO_STATUS_NO_MEMORY; - font = _win32_font_create (&logfont, scale); - if (!font) + scaled_font = _win32_scaled_font_create (&logfont, font_matrix, ctm); + if (!scaled_font) return CAIRO_STATUS_NO_MEMORY; - *font_out = font; + *scaled_font_out = scaled_font; return CAIRO_STATUS_SUCCESS; } static void -_cairo_win32_font_destroy_font (void *abstract_font) +_cairo_win32_scaled_font_destroy (void *abstract_font) { - cairo_win32_font_t *font = abstract_font; + cairo_win32_scaled_font_t *scaled_font = abstract_font; - if (font->scaled_font) - DeleteObject (font->scaled_font); + if (scaled_font->scaled_hfont) + DeleteObject (scaled_font->scaled_hfont); - if (font->unscaled_font) - DeleteObject (font->unscaled_font); - - free (font); -} - -static void -_cairo_win32_font_destroy_unscaled_font (void *abstract_font) -{ + if (scaled_font->unscaled_hfont) + DeleteObject (scaled_font->unscaled_hfont); } static void -_cairo_win32_font_get_glyph_cache_key (void *abstract_font, - cairo_glyph_cache_key_t *key) +_cairo_win32_scaled_font_get_glyph_cache_key (void *abstract_font, + cairo_glyph_cache_key_t *key) { } static cairo_status_t -_cairo_win32_font_text_to_glyphs (void *abstract_font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) +_cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - cairo_win32_font_t *font = abstract_font; + cairo_win32_scaled_font_t *scaled_font = abstract_font; uint16_t *utf16; int n16; GCP_RESULTSW gcp_results; @@ -524,7 +517,7 @@ _cairo_win32_font_text_to_glyphs (void *abstract_font, goto FAIL1; } - status = cairo_win32_font_select_font (&font->base, hdc); + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (!CAIRO_OK (status)) goto FAIL1; @@ -553,7 +546,7 @@ _cairo_win32_font_text_to_glyphs (void *abstract_font, 0, &gcp_results, GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_font_text_to_glyphs"); + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs"); goto FAIL2; } @@ -582,7 +575,7 @@ _cairo_win32_font_text_to_glyphs (void *abstract_font, (*glyphs)[i].x = x_pos ; (*glyphs)[i].y = 0; - x_pos += dx[i] / font->logical_scale; + x_pos += dx[i] / scaled_font->logical_scale; } FAIL2: @@ -591,7 +584,7 @@ _cairo_win32_font_text_to_glyphs (void *abstract_font, if (dx) free (dx); - cairo_win32_font_done_font (&font->base); + cairo_win32_scaled_font_done_font (&scaled_font->base); FAIL1: free (utf16); @@ -600,10 +593,10 @@ _cairo_win32_font_text_to_glyphs (void *abstract_font, } static cairo_status_t -_cairo_win32_font_font_extents (void *abstract_font, - cairo_font_extents_t *extents) +_cairo_win32_scaled_font_font_extents (void *abstract_font, + cairo_font_extents_t *extents) { - cairo_win32_font_t *font = abstract_font; + cairo_win32_scaled_font_t *scaled_font = abstract_font; cairo_status_t status; TEXTMETRIC metrics; HDC hdc; @@ -612,21 +605,21 @@ _cairo_win32_font_font_extents (void *abstract_font, if (!hdc) return CAIRO_STATUS_NO_MEMORY; - if (font->preserve_axes) { + if (scaled_font->preserve_axes) { /* For 90-degree rotations (including 0), we get the metrics * from the GDI in logical space, then convert back to font space */ - status = cairo_win32_font_select_font (&font->base, hdc); + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (!CAIRO_OK (status)) return status; GetTextMetrics (hdc, &metrics); - cairo_win32_font_done_font (&font->base); + cairo_win32_scaled_font_done_font (&scaled_font->base); - extents->ascent = metrics.tmAscent / font->logical_scale; - extents->descent = metrics.tmDescent / font->logical_scale; + extents->ascent = metrics.tmAscent / scaled_font->logical_scale; + extents->descent = metrics.tmDescent / scaled_font->logical_scale; - extents->height = (metrics.tmHeight + metrics.tmExternalLeading) / font->logical_scale; - extents->max_x_advance = metrics.tmMaxCharWidth / font->logical_scale; + extents->height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale; + extents->max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale; extents->max_y_advance = 0; } else { @@ -635,16 +628,16 @@ _cairo_win32_font_font_extents (void *abstract_font, * transformed font are inexplicably large and we want to * avoid them. */ - status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); if (!CAIRO_OK (status)) return status; GetTextMetrics (hdc, &metrics); - _cairo_win32_font_done_unscaled_font (&font->base); + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); - extents->ascent = (double)metrics.tmAscent / font->em_square; - extents->descent = metrics.tmDescent * font->em_square; - extents->height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / font->em_square; - extents->max_x_advance = (double)(metrics.tmMaxCharWidth) / font->em_square; + extents->ascent = (double)metrics.tmAscent / scaled_font->em_square; + extents->descent = metrics.tmDescent * scaled_font->em_square; + extents->height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square; + extents->max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square; extents->max_y_advance = 0; } @@ -653,12 +646,12 @@ _cairo_win32_font_font_extents (void *abstract_font, } static cairo_status_t -_cairo_win32_font_glyph_extents (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_win32_scaled_font_glyph_extents (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - cairo_win32_font_t *font = abstract_font; + cairo_win32_scaled_font_t *scaled_font = abstract_font; static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; GLYPHMETRICS metrics; cairo_status_t status; @@ -674,39 +667,39 @@ _cairo_win32_font_glyph_extents (void *abstract_font, */ assert (num_glyphs == 1); - if (font->preserve_axes) { + if (scaled_font->preserve_axes) { /* If we aren't rotating / skewing the axes, then we get the metrics * from the GDI in device space and convert to font space. */ - status = cairo_win32_font_select_font (&font->base, hdc); + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (!CAIRO_OK (status)) return status; GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, &metrics, 0, NULL, &matrix); - cairo_win32_font_done_font (&font->base); - - if (font->swap_axes) { - extents->x_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; - extents->y_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; - extents->width = metrics.gmBlackBoxY / font->y_scale; - extents->height = metrics.gmBlackBoxX / font->x_scale; - extents->x_advance = metrics.gmCellIncY / font->x_scale; - extents->y_advance = metrics.gmCellIncX / font->y_scale; + cairo_win32_scaled_font_done_font (&scaled_font->base); + + if (scaled_font->swap_axes) { + extents->x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents->y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents->width = metrics.gmBlackBoxY / scaled_font->y_scale; + extents->height = metrics.gmBlackBoxX / scaled_font->x_scale; + extents->x_advance = metrics.gmCellIncY / scaled_font->x_scale; + extents->y_advance = metrics.gmCellIncX / scaled_font->y_scale; } else { - extents->x_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; - extents->y_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; - extents->width = metrics.gmBlackBoxX / font->x_scale; - extents->height = metrics.gmBlackBoxY / font->y_scale; - extents->x_advance = metrics.gmCellIncX / font->x_scale; - extents->y_advance = metrics.gmCellIncY / font->y_scale; + extents->x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents->y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents->width = metrics.gmBlackBoxX / scaled_font->x_scale; + extents->height = metrics.gmBlackBoxY / scaled_font->y_scale; + extents->x_advance = metrics.gmCellIncX / scaled_font->x_scale; + extents->y_advance = metrics.gmCellIncY / scaled_font->y_scale; } - if (font->swap_x) { + if (scaled_font->swap_x) { extents->x_bearing = (- extents->x_bearing - extents->width); extents->x_advance = - extents->x_advance; } - if (font->swap_y) { + if (scaled_font->swap_y) { extents->y_bearing = (- extents->y_bearing - extents->height); extents->y_advance = - extents->y_advance; } @@ -715,17 +708,17 @@ _cairo_win32_font_glyph_extents (void *abstract_font, /* For all other transformations, we use the design metrics * of the font. */ - status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, &metrics, 0, NULL, &matrix); - _cairo_win32_font_done_unscaled_font (&font->base); - - extents->x_bearing = (double)metrics.gmptGlyphOrigin.x / font->em_square; - extents->y_bearing = (double)metrics.gmptGlyphOrigin.y / font->em_square; - extents->width = (double)metrics.gmBlackBoxX / font->em_square; - extents->height = (double)metrics.gmBlackBoxY / font->em_square; - extents->x_advance = (double)metrics.gmCellIncX / font->em_square; - extents->y_advance = (double)metrics.gmCellIncY / font->em_square; + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents->x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square; + extents->y_bearing = (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square; + extents->width = (double)metrics.gmBlackBoxX / scaled_font->em_square; + extents->height = (double)metrics.gmBlackBoxY / scaled_font->em_square; + extents->x_advance = (double)metrics.gmCellIncX / scaled_font->em_square; + extents->y_advance = (double)metrics.gmCellIncY / scaled_font->em_square; } return CAIRO_STATUS_SUCCESS; @@ -733,13 +726,13 @@ _cairo_win32_font_glyph_extents (void *abstract_font, static cairo_status_t -_cairo_win32_font_glyph_bbox (void *abstract_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_win32_scaled_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; - cairo_win32_font_t *font = abstract_font; + cairo_win32_scaled_font_t *scaled_font = abstract_font; int x1 = 0, x2 = 0, y1 = 0, y2 = 0; if (num_glyphs > 0) { @@ -751,7 +744,7 @@ _cairo_win32_font_glyph_bbox (void *abstract_font, if (!hdc) return CAIRO_STATUS_NO_MEMORY; - status = cairo_win32_font_select_font (&font->base, hdc); + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (!CAIRO_OK (status)) return status; @@ -772,7 +765,7 @@ _cairo_win32_font_glyph_bbox (void *abstract_font, y2 = y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY; } - cairo_win32_font_done_font (&font->base); + cairo_win32_scaled_font_done_font (&scaled_font->base); } bbox->p1.x = _cairo_fixed_from_int (x1); @@ -784,7 +777,7 @@ _cairo_win32_font_glyph_bbox (void *abstract_font, } typedef struct { - cairo_win32_font_t *font; + cairo_win32_scaled_font_t *scaled_font; HDC hdc; cairo_array_t glyphs; @@ -796,12 +789,12 @@ typedef struct { } cairo_glyph_state_t; static void -_start_glyphs (cairo_glyph_state_t *state, - cairo_win32_font_t *font, - HDC hdc) +_start_glyphs (cairo_glyph_state_t *state, + cairo_win32_scaled_font_t *scaled_font, + HDC hdc) { state->hdc = hdc; - state->font = font; + state->scaled_font = scaled_font; _cairo_array_init (&state->glyphs, sizeof (WCHAR)); _cairo_array_init (&state->dx, sizeof (int)); @@ -841,7 +834,7 @@ _add_glyph (cairo_glyph_state_t *state, WCHAR glyph_index = index; int logical_x, logical_y; - cairo_matrix_transform_point (&state->font->device_to_logical, &user_x, &user_y); + cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y); logical_x = floor (user_x + 0.5); logical_y = floor (user_y + 0.5); @@ -881,13 +874,13 @@ _finish_glyphs (cairo_glyph_state_t *state) } static cairo_status_t -_draw_glyphs_on_surface (cairo_win32_surface_t *surface, - cairo_win32_font_t *font, - COLORREF color, - int x_offset, - int y_offset, - const cairo_glyph_t *glyphs, - int num_glyphs) +_draw_glyphs_on_surface (cairo_win32_surface_t *surface, + cairo_win32_scaled_font_t *scaled_font, + COLORREF color, + int x_offset, + int y_offset, + const cairo_glyph_t *glyphs, + int num_glyphs) { cairo_glyph_state_t state; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -896,7 +889,7 @@ _draw_glyphs_on_surface (cairo_win32_surface_t *surface, if (!SaveDC (surface->dc)) return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); - status = cairo_win32_font_select_font (&font->base, surface->dc); + status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); if (!CAIRO_OK (status)) goto FAIL1; @@ -904,7 +897,7 @@ _draw_glyphs_on_surface (cairo_win32_surface_t *surface, SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); SetBkMode (surface->dc, TRANSPARENT); - _start_glyphs (&state, font, surface->dc); + _start_glyphs (&state, scaled_font, surface->dc); for (i = 0; i < num_glyphs; i++) { status = _add_glyph (&state, glyphs[i].index, @@ -915,7 +908,7 @@ _draw_glyphs_on_surface (cairo_win32_surface_t *surface, FAIL2: _finish_glyphs (&state); - cairo_win32_font_done_font (&font->base); + cairo_win32_scaled_font_done_font (&scaled_font->base); FAIL1: RestoreDC (surface->dc, 1); @@ -986,20 +979,20 @@ _compute_a8_mask (cairo_win32_surface_t *mask_surface) } static cairo_status_t -_cairo_win32_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *generic_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_win32_scaled_font_show_glyphs (void *abstract_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) { - cairo_win32_font_t *font = abstract_font; + cairo_win32_scaled_font_t *scaled_font = abstract_font; cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; cairo_status_t status; @@ -1009,8 +1002,7 @@ _cairo_win32_font_show_glyphs (void *abstract_font, if (_cairo_surface_is_win32 (generic_surface) && surface->format == CAIRO_FORMAT_RGB24 && operator == CAIRO_OPERATOR_OVER && - pattern->type == CAIRO_PATTERN_SOLID && - _cairo_pattern_is_opaque (pattern)) { + _cairo_pattern_is_opaque_solid (pattern)) { cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; @@ -1019,11 +1011,12 @@ _cairo_win32_font_show_glyphs (void *abstract_font, */ COLORREF new_color; - new_color = RGB (((int)(0xffff * solid_pattern->red)) >> 8, - ((int)(0xffff * solid_pattern->green)) >> 8, - ((int)(0xffff * solid_pattern->blue)) >> 8); + /* XXX Use the unpremultiplied or premultiplied color? */ + new_color = RGB (((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); - status = _draw_glyphs_on_surface (surface, font, new_color, + status = _draw_glyphs_on_surface (surface, scaled_font, new_color, 0, 0, glyphs, num_glyphs); @@ -1050,11 +1043,11 @@ _cairo_win32_font_show_glyphs (void *abstract_font, r.bottom = height; FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); - _draw_glyphs_on_surface (tmp_surface, font, RGB (0, 0, 0), + _draw_glyphs_on_surface (tmp_surface, scaled_font, RGB (0, 0, 0), dest_x, dest_y, glyphs, num_glyphs); - if (font->quality == CLEARTYPE_QUALITY) { + if (scaled_font->quality == CLEARTYPE_QUALITY) { /* For ClearType, we need a 4-channel mask. If we are compositing on * a surface with alpha, we need to compute the alpha channel of * the mask (we just copy the green channel). But for a destination @@ -1101,84 +1094,109 @@ _cairo_win32_font_show_glyphs (void *abstract_font, } static cairo_status_t -_cairo_win32_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_win32_scaled_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) { return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_win32_font_create_glyph (cairo_image_glyph_cache_entry_t *val) -{ - return CAIRO_STATUS_NO_MEMORY; -} +const cairo_scaled_font_backend_t cairo_win32_scaled_font_backend = { + _cairo_win32_scaled_font_create, + _cairo_win32_scaled_font_destroy, + _cairo_win32_scaled_font_font_extents, + _cairo_win32_scaled_font_text_to_glyphs, + _cairo_win32_scaled_font_glyph_extents, + _cairo_win32_scaled_font_glyph_bbox, + _cairo_win32_scaled_font_show_glyphs, + _cairo_win32_scaled_font_glyph_path, + _cairo_win32_scaled_font_get_glyph_cache_key, +}; + +/* cairo_win32_font_face_t */ -const cairo_font_backend_t cairo_win32_font_backend = { - _cairo_win32_font_create, - _cairo_win32_font_destroy_font, - _cairo_win32_font_destroy_unscaled_font, - _cairo_win32_font_font_extents, - _cairo_win32_font_text_to_glyphs, - _cairo_win32_font_glyph_extents, - _cairo_win32_font_glyph_bbox, - _cairo_win32_font_show_glyphs, - _cairo_win32_font_glyph_path, - _cairo_win32_font_get_glyph_cache_key, - _cairo_win32_font_create_glyph +typedef struct _cairo_win32_font_face cairo_win32_font_face_t; + +struct _cairo_win32_font_face { + cairo_font_face_t base; + LOGFONTW logfont; }; /* implement the platform-specific interface */ +static void +_cairo_win32_font_face_destroy (void *abstract_face) +{ +} + +static cairo_status_t +_cairo_win32_font_face_create_font (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **font) +{ + cairo_win32_font_face_t *font_face = abstract_face; + + *font = _win32_scaled_font_create (&font_face->logfont, + font_matrix, ctm); + if (*font) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_STATUS_NO_MEMORY; +} + +static const cairo_font_face_backend_t _cairo_win32_font_face_backend = { + _cairo_win32_font_face_destroy, + _cairo_win32_font_face_create_font, +}; + /** - * cairo_win32_font_create_for_logfontw: + * cairo_win32_scaled_font_create_for_logfontw: * @logfont: A #LOGFONTW structure specifying the font to use. * The lfHeight, lfWidth, lfOrientation and lfEscapement - * fields of this structure are ignored; information from - * @scale will be used instead. - * @scale: The scale at which this font will be used. The - * scale is given by multiplying the font matrix (see - * cairo_transform_font()) by the current transformation matrix. - * The translation elements of the resulting matrix are ignored. + * fields of this structure are ignored. * * Creates a new font for the Win32 font backend based on a * #LOGFONT. This font can then be used with - * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend - * specific functions like cairo_win32_font_select_font(). + * cairo_set_font_face() or cairo_font_create(). The #cairo_scaled_font_t + * returned from cairo_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). * - * Return value: a newly created #cairo_font_t. Free with - * cairo_font_destroy() when you are done using it. + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. **/ -cairo_font_t * -cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, - cairo_matrix_t *scale) +cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) { - cairo_font_scale_t sc; + cairo_win32_font_face_t *font_face; + + font_face = malloc (sizeof (cairo_win32_font_face_t)); + if (!font_face) + return NULL; + + font_face->logfont = *logfont; - cairo_matrix_get_affine (scale, - &sc.matrix[0][0], &sc.matrix[0][1], - &sc.matrix[1][0], &sc.matrix[1][1], - NULL, NULL); + _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); - return _win32_font_create (logfont, &sc); + return &font_face->base; } /** - * cairo_win32_font_select_font: - * @font: A #cairo_font_t from the Win32 font backend. Such an - * object can be created with cairo_win32_font_create_for_logfontw(). + * cairo_win32_scaled_font_select_font: + * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an + * object can be created with cairo_win32_scaled_font_create_for_logfontw(). * @hdc: a device context * * Selects the font into the given device context and changes the * map mode and world transformation of the device context to match * that of the font. This function is intended for use when using * layout APIs such as Uniscribe to do text layout with the - * Cairo font. After finishing using the device context, you must call - * cairo_win32_font_done_font() to release any resources allocated + * cairo font. After finishing using the device context, you must call + * cairo_win32_scaled_font_done_font() to release any resources allocated * by this function. * - * See cairo_win32_font_get_scale_factor() for converting logical + * See cairo_win32_scaled_font_get_metrics_factor() for converting logical * coordinates from the device context to font space. * * Normally, calls to SaveDC() and RestoreDC() would be made around @@ -1189,30 +1207,30 @@ cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, * the device context is unchanged. **/ cairo_status_t -cairo_win32_font_select_font (cairo_font_t *font, - HDC hdc) +cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, + HDC hdc) { cairo_status_t status; HFONT hfont; HFONT old_hfont = NULL; int old_mode; - hfont = _win32_font_get_scaled_font ((cairo_win32_font_t *)font); + hfont = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font); if (!hfont) return CAIRO_STATUS_NO_MEMORY; old_hfont = SelectObject (hdc, hfont); if (!old_hfont) - return _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); + return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font"); old_mode = SetGraphicsMode (hdc, GM_ADVANCED); if (!old_mode) { - status = _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); + status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font"); SelectObject (hdc, old_hfont); return status; } - status = _win32_font_set_world_transform ((cairo_win32_font_t *)font, hdc); + status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); if (!CAIRO_OK (status)) { SetGraphicsMode (hdc, old_mode); SelectObject (hdc, old_hfont); @@ -1225,28 +1243,30 @@ cairo_win32_font_select_font (cairo_font_t *font, } /** - * cairo_win32_font_done_font: - * @font: A #cairo_font_t from the Win32 font backend. + * cairo_win32_scaled_font_done_font: + * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. * - * Releases any resources allocated by cairo_win32_font_select_font() + * Releases any resources allocated by cairo_win32_scaled_font_select_font() **/ void -cairo_win32_font_done_font (cairo_font_t *font) +cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) { } /** - * cairo_win32_font_get_scale_factor: - * @font: a #cairo_font_t from the Win32 font backend + * cairo_win32_scaled_font_get_metrics_factor: + * @scaled_font: a #cairo_scaled_font_t from the Win32 font backend * * Gets a scale factor between logical coordinates in the coordinate - * space used by cairo_win32_font_select_font() and font space coordinates. + * space used by cairo_win32_scaled_font_select_font() (that is, the + * coordinate system used by the Windows functions to return metrics) and + * font space coordinates. * * Return value: factor to multiply logical units by to get font space * coordinates. **/ double -cairo_win32_font_get_scale_factor (cairo_font_t *font) +cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font) { - return 1. / ((cairo_win32_font_t *)font)->logical_scale; + return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale; } diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c index dcfe6d044..9df86380b 100644 --- a/src/cairo-win32-surface.c +++ b/src/cairo-win32-surface.c @@ -1,6 +1,6 @@ /* Cairo - a vector graphics library with display and print output * - * Copyright © 2005 Red Hat, Inc. + * Copyright 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -34,7 +34,7 @@ */ #include <stdio.h> - +#include "cairoint.h" #include "cairo-win32-private.h" static const cairo_surface_backend_t cairo_win32_surface_backend; @@ -46,7 +46,7 @@ static const cairo_surface_backend_t cairo_win32_surface_backend; * Helper function to dump out a human readable form of the * current error code. * - * Return value: A Cairo status code for the error code + * Return value: A cairo status code for the error code **/ cairo_status_t _cairo_win32_print_gdi_error (const char *context) @@ -76,27 +76,6 @@ _cairo_win32_print_gdi_error (const char *context) return CAIRO_STATUS_NO_MEMORY; } -void -cairo_set_target_win32 (cairo_t *cr, - HDC hdc) -{ - cairo_surface_t *surface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_win32_surface_create (hdc); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - static cairo_status_t _create_dc_and_bitmap (cairo_win32_surface_t *surface, HDC original_dc, @@ -215,7 +194,7 @@ _create_dc_and_bitmap (cairo_win32_surface_t *surface, *bits_out = bits; if (rowstride_out) { - /* Windows bitmaps are padded to 16-bit (word) boundaries */ + /* Windows bitmaps are padded to 32-bit (dword) boundaries */ switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: @@ -223,11 +202,11 @@ _create_dc_and_bitmap (cairo_win32_surface_t *surface, break; case CAIRO_FORMAT_A8: - *rowstride_out = (width + 1) & -2; + *rowstride_out = (width + 3) & ~3; break; case CAIRO_FORMAT_A1: - *rowstride_out = ((width + 15) & -16) / 8; + *rowstride_out = ((width + 31) & ~31) / 8; break; } } @@ -345,8 +324,8 @@ _cairo_win32_surface_create_dib (cairo_format_t format, width, height); } -static void -_cairo_win32_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_win32_surface_finish (void *abstract_surface) { cairo_win32_surface_t *surface = abstract_surface; @@ -362,15 +341,8 @@ _cairo_win32_surface_destroy (void *abstract_surface) DeleteObject (surface->bitmap); DeleteDC (surface->dc); } - - free (surface); -} -static double -_cairo_win32_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We should really get this value from somewhere */ - return 96.0; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -433,10 +405,6 @@ _cairo_win32_surface_acquire_source_image (void *abstract_sur surface->clip_rect.width, surface->clip_rect.height, &local); if (CAIRO_OK (status)) { - cairo_surface_set_filter (&local->base, surface->base.filter); - cairo_surface_set_matrix (&local->base, &surface->base.matrix); - cairo_surface_set_repeat (&local->base, surface->base.repeat); - *image_out = (cairo_image_surface_t *)local->image; *image_extra = local; } @@ -544,14 +512,6 @@ _cairo_win32_surface_release_dest_image (void *abstract_surfac cairo_surface_destroy ((cairo_surface_t *)local); } -static cairo_status_t -_cairo_win32_surface_clone_similar (void *surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static cairo_int_status_t _cairo_win32_surface_composite (cairo_operator_t operator, cairo_pattern_t *pattern, @@ -584,9 +544,9 @@ _cairo_win32_surface_composite (cairo_operator_t operator, if (mask_pattern->type != CAIRO_PATTERN_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; - alpha = (int)(0xffff * pattern->alpha * mask_pattern->alpha) >> 8; + alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; } else { - alpha = (int)(0xffff * pattern->alpha) >> 8; + alpha = 255; } src_surface_pattern = (cairo_surface_pattern_t *)pattern; @@ -601,7 +561,7 @@ _cairo_win32_surface_composite (cairo_operator_t operator, if (alpha == 255 && src->format == dst->format && - (operator == CAIRO_OPERATOR_SRC || + (operator == CAIRO_OPERATOR_SOURCE || (src->format == CAIRO_FORMAT_RGB24 && operator == CAIRO_OPERATOR_OVER))) { if (!BitBlt (dst->dc, @@ -642,6 +602,81 @@ _cairo_win32_surface_composite (cairo_operator_t operator, return CAIRO_INT_STATUS_UNSUPPORTED; } +/* This big function tells us how to optimize operators for the + * case of solid destination and constant-alpha source + * + * NOTE: This function needs revisiting if we add support for + * super-luminescent colors (a == 0, r,g,b > 0) + */ +static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED } +categorize_solid_dest_operator (cairo_operator_t operator, + unsigned short alpha) +{ + enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source; + + if (alpha >= 0xff00) + source = SOURCE_SOLID; + else if (alpha < 0x100) + source = SOURCE_TRANSPARENT; + else + source = SOURCE_OTHER; + + switch (operator) { + case CAIRO_OPERATOR_CLEAR: /* 0 0 */ + case CAIRO_OPERATOR_OUT: /* 1 - Ab 0 */ + return DO_CLEAR; + break; + + case CAIRO_OPERATOR_SOURCE: /* 1 0 */ + case CAIRO_OPERATOR_IN: /* Ab 0 */ + return DO_SOURCE; + break; + + case CAIRO_OPERATOR_OVER: /* 1 1 - Aa */ + case CAIRO_OPERATOR_ATOP: /* Ab 1 - Aa */ + if (source == SOURCE_SOLID) + return DO_SOURCE; + else if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_DEST_OUT: /* 0 1 - Aa */ + case CAIRO_OPERATOR_XOR: /* 1 - Ab 1 - Aa */ + if (source == SOURCE_SOLID) + return DO_CLEAR; + else if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_DEST: /* 0 1 */ + case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab 1 */ + case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa) 1 */ + return DO_NOTHING; + break; + + case CAIRO_OPERATOR_DEST_IN: /* 0 Aa */ + case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab Aa */ + if (source == SOURCE_SOLID) + return DO_NOTHING; + else if (source == SOURCE_TRANSPARENT) + return DO_CLEAR; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_ADD: /* 1 1 */ + if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + } +} + static cairo_int_status_t _cairo_win32_surface_fill_rectangles (void *abstract_surface, cairo_operator_t operator, @@ -660,17 +695,24 @@ _cairo_win32_surface_fill_rectangles (void *abstract_surface, */ if (surface->image) return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We could support possibly support more operators for color->alpha = 0xffff. - * for CAIRO_OPERATOR_SRC, alpha doesn't matter since we know the destination - * image doesn't have alpha. (surface->pixman_image is non-NULL for all + + /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all * surfaces with alpha.) */ - if (operator != CAIRO_OPERATOR_SRC) + switch (categorize_solid_dest_operator (operator, color->alpha_short)) { + case DO_CLEAR: + new_color = RGB (0, 0, 0); + break; + case DO_SOURCE: + new_color = RGB (color->red_short >> 8, color->blue_short >> 8, color->green_short >> 8); + break; + case DO_NOTHING: + return CAIRO_STATUS_SUCCESS; + case DO_UNSUPPORTED: + default: return CAIRO_INT_STATUS_UNSUPPORTED; - - new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); - + } + new_brush = CreateSolidBrush (new_color); if (!new_brush) return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); @@ -700,35 +742,6 @@ _cairo_win32_surface_fill_rectangles (void *abstract_surface, } static cairo_int_status_t -_cairo_win32_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) - -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t _cairo_win32_surface_set_clip_region (void *abstract_surface, pixman_region16_t *region) { @@ -741,14 +754,14 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, if (surface->image) _cairo_surface_set_clip_region (surface->image, region); - /* The semantics we want is that any clip set by Cairo combines + /* The semantics we want is that any clip set by cairo combines * is intersected with the clip on device context that the * surface was created for. To implement this, we need to * save the original clip when first setting a clip on surface. */ if (region == NULL) { - /* Clear any clip set by Cairo, return to the original */ + /* Clear any clip set by cairo, return to the original */ if (surface->set_clip) { if (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR) @@ -845,21 +858,22 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, } } -static cairo_status_t -_cairo_win32_surface_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) +static cairo_int_status_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) { - return CAIRO_INT_STATUS_UNSUPPORTED; + cairo_win32_surface_t *surface = abstract_surface; + RECT clip_box; + + if (GetClipBox (surface->dc, &clip_box) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image"); + + rectangle->x = clip_box.left; + rectangle->y = clip_box.top; + rectangle->width = clip_box.right - clip_box.left; + rectangle->height = clip_box.bottom - clip_box.top; + + return CAIRO_STATUS_SUCCESS; } cairo_surface_t * @@ -884,6 +898,7 @@ cairo_win32_surface_create (HDC hdc) surface->dc = hdc; surface->bitmap = NULL; + surface->saved_dc_bitmap = NULL; surface->clip_rect.x = rect.left; surface->clip_rect.y = rect.top; @@ -914,18 +929,18 @@ _cairo_surface_is_win32 (cairo_surface_t *surface) static const cairo_surface_backend_t cairo_win32_surface_backend = { _cairo_win32_surface_create_similar, - _cairo_win32_surface_destroy, - _cairo_win32_surface_pixels_per_inch, + _cairo_win32_surface_finish, _cairo_win32_surface_acquire_source_image, _cairo_win32_surface_release_source_image, _cairo_win32_surface_acquire_dest_image, _cairo_win32_surface_release_dest_image, - _cairo_win32_surface_clone_similar, + NULL, /* clone_similar */ _cairo_win32_surface_composite, _cairo_win32_surface_fill_rectangles, - _cairo_win32_surface_composite_trapezoids, - _cairo_win32_surface_copy_page, - _cairo_win32_surface_show_page, + NULL, /* composite_trapezoids */ + NULL, /* copy_page */ + NULL, /* show_page */ _cairo_win32_surface_set_clip_region, - _cairo_win32_surface_show_glyphs + _cairo_win32_surface_get_extents, + NULL /* show_glyphs */ }; diff --git a/src/cairo-win32.h b/src/cairo-win32.h index fab497aa4..93983456f 100644 --- a/src/cairo-win32.h +++ b/src/cairo-win32.h @@ -34,38 +34,36 @@ */ #ifndef _CAIRO_WIN32_H_ +#define _CAIRO_WIN32_H_ #include <cairo.h> -#ifdef CAIRO_HAS_WIN32_SURFACE +#if CAIRO_HAS_WIN32_SURFACE #include <windows.h> CAIRO_BEGIN_DECLS -void -cairo_set_target_win32 (cairo_t *cr, - HDC hdc); - cairo_surface_t * cairo_win32_surface_create (HDC hdc); -cairo_font_t * -cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, - cairo_matrix_t *scale); +cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont); cairo_status_t -cairo_win32_font_select_font (cairo_font_t *font, - HDC hdc); +cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, + HDC hdc); void -cairo_win32_font_done_font (cairo_font_t *font); +cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font); double -cairo_win32_font_get_scale_factor (cairo_font_t *font); - -#endif /* CAIRO_HAS_WIN32_SURFACE */ +cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font); CAIRO_END_DECLS +#else /* CAIRO_HAS_WIN32_SURFACE */ +# error Cairo was not compiled with support for the win32 backend +#endif /* CAIRO_HAS_WIN32_SURFACE */ + #endif /* _CAIRO_WIN32_H_ */ diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index 0694b77a2..95568d608 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -36,12 +36,7 @@ #include "cairoint.h" #include "cairo-xcb.h" - -cairo_surface_t * -cairo_xcb_surface_create (XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format); +#include "cairo-xcb-xrender.h" #define AllPlanes ((unsigned long)~0L) @@ -71,10 +66,12 @@ format_from_visual(XCBConnection *c, XCBVISUALID visual) return nil; } -static XCBRenderPICTFORMAT -format_from_cairo(XCBConnection *c, cairo_format_t fmt) +/* XXX: Why is this ridiculously complex compared to the equivalent + * function in cairo-xlib-surface.c */ +static XCBRenderPICTFORMINFO +_format_from_cairo(XCBConnection *c, cairo_format_t fmt) { - XCBRenderPICTFORMAT ret = { 0 }; + XCBRenderPICTFORMINFO ret = {{ 0 }}; struct tmpl_t { XCBRenderDIRECTFORMAT direct; CARD8 depth; @@ -151,36 +148,20 @@ format_from_cairo(XCBConnection *c, cairo_format_t fmt) if(t->alpha_mask && (t->alpha_mask != f->alpha_mask || t->alpha_shift != f->alpha_shift)) continue; - ret = fi.data->id; + ret = *fi.data; } free(r); return ret; } -void -cairo_set_target_xcb (cairo_t *cr, - XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format) -{ - cairo_surface_t *surface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_xcb_surface_create (dpy, drawable, visual, format); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); +/* + * Instead of taking two round trips for each blending request, + * assume that if a particular drawable fails GetImage that it will + * fail for a "while"; use temporary pixmaps to avoid the errors + */ - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} +#define CAIRO_ASSUME_PIXMAP 20 typedef struct cairo_xcb_surface { cairo_surface_t base; @@ -190,15 +171,19 @@ typedef struct cairo_xcb_surface { XCBDRAWABLE drawable; int owns_pixmap; XCBVISUALTYPE *visual; - cairo_format_t format; + + int use_pixmap; int render_major; int render_minor; int width; int height; + int depth; XCBRenderPICTURE picture; + XCBRenderPICTFORMINFO format; + int has_format; } cairo_xcb_surface_t; #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ @@ -222,6 +207,9 @@ typedef struct cairo_xcb_surface { #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +static void +_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface); + static int _CAIRO_FORMAT_DEPTH (cairo_format_t format) { @@ -239,46 +227,43 @@ _CAIRO_FORMAT_DEPTH (cairo_format_t format) } static cairo_surface_t * -_cairo_xcb_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) +_cairo_xcb_surface_create_similar (void *abstract_src, + cairo_format_t format, + int drawable, + int width, + int height) { cairo_xcb_surface_t *src = abstract_src; XCBConnection *dpy = src->dpy; XCBDRAWABLE d; cairo_xcb_surface_t *surface; + XCBRenderPICTFORMINFO xrender_format = _format_from_cairo (dpy, format); - /* XXX: There's a pretty lame heuristic here. This assumes that - * all non-Render X servers do not support depth-32 pixmaps, (and - * that they do support depths 1, 8, and 24). Obviously, it would - * be much better to check the depths that are actually - * supported. */ - if (!dpy - || (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src) - && format == CAIRO_FORMAT_ARGB32)) - { - return NULL; + /* As a good first approximation, if the display doesn't have COMPOSITE, + * we're better off using image surfaces for all temporary operations + */ + if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) { + return cairo_image_surface_create (format, width, height); } d.pixmap = XCBPIXMAPNew (dpy); XCBCreatePixmap (dpy, _CAIRO_FORMAT_DEPTH (format), d.pixmap, src->drawable, - width, height); + width <= 0 ? 1 : width, + height <= 0 ? 1 : height); surface = (cairo_xcb_surface_t *) - cairo_xcb_surface_create (dpy, d, NULL, format); - surface->owns_pixmap = 1; + cairo_xcb_surface_create_with_xrender_format (dpy, d, + &xrender_format, + width, height); - surface->width = width; - surface->height = height; + surface->owns_pixmap = TRUE; return &surface->base; } -static void -_cairo_xcb_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_xcb_surface_finish (void *abstract_surface) { cairo_xcb_surface_t *surface = abstract_surface; if (surface->picture.xid) @@ -290,20 +275,13 @@ _cairo_xcb_surface_destroy (void *abstract_surface) if (surface->gc.xid) XCBFreeGC (surface->dpy, surface->gc); - surface->dpy = 0; + surface->dpy = NULL; - free (surface); -} - -static double -_cairo_xcb_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We should really get this value from somewhere like Xft.dpy */ - return 96.0; + return CAIRO_STATUS_SUCCESS; } static int -bits_per_pixel(XCBConnection *c, int depth) +_bits_per_pixel(XCBConnection *c, int depth) { XCBFORMAT *fmt = XCBConnSetupSuccessRepPixmapFormats(XCBGetSetup(c)); XCBFORMAT *fmtend = fmt + XCBConnSetupSuccessRepPixmapFormatsLength(XCBGetSetup(c)); @@ -322,12 +300,52 @@ bits_per_pixel(XCBConnection *c, int depth) } static int -bytes_per_line(XCBConnection *c, int width, int bpp) +_bytes_per_line(XCBConnection *c, int width, int bpp) { int bitmap_pad = XCBGetSetup(c)->bitmap_format_scanline_pad; return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3; } +static cairo_bool_t +_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) +{ + switch (masks->bpp) { + case 32: + if (masks->alpha_mask == 0xff000000 && + masks->red_mask == 0x00ff0000 && + masks->green_mask == 0x0000ff00 && + masks->blue_mask == 0x000000ff) + { + *format = CAIRO_FORMAT_ARGB32; + return 1; + } + if (masks->alpha_mask == 0x00000000 && + masks->red_mask == 0x00ff0000 && + masks->green_mask == 0x0000ff00 && + masks->blue_mask == 0x000000ff) + { + *format = CAIRO_FORMAT_RGB24; + return 1; + } + break; + case 8: + if (masks->alpha_mask == 0xff) + { + *format = CAIRO_FORMAT_A8; + return 1; + } + break; + case 1: + if (masks->alpha_mask == 0x1) + { + *format = CAIRO_FORMAT_A1; + return 1; + } + break; + } + return 0; +} + static cairo_status_t _get_image_surface (cairo_xcb_surface_t *surface, cairo_rectangle_t *interest_rect, @@ -335,18 +353,12 @@ _get_image_surface (cairo_xcb_surface_t *surface, cairo_rectangle_t *image_rect) { cairo_image_surface_t *image; - XCBGetGeometryRep *geomrep; XCBGetImageRep *imagerep; - int bpp; + int bpp, bytes_per_line; int x1, y1, x2, y2; - - geomrep = XCBGetGeometryReply(surface->dpy, XCBGetGeometry(surface->dpy, surface->drawable), 0); - if(!geomrep) - return 0; - - surface->width = geomrep->width; - surface->height = geomrep->height; - free(geomrep); + unsigned char *data; + cairo_format_t format; + cairo_format_masks_t masks; x1 = 0; y1 = 0; @@ -354,14 +366,21 @@ _get_image_surface (cairo_xcb_surface_t *surface, y2 = surface->height; if (interest_rect) { - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if (interest_rect->x + interest_rect->width < x2) - x2 = interest_rect->x + interest_rect->width; - if (interest_rect->y + interest_rect->height < y2) - y2 = interest_rect->y + interest_rect->height; + cairo_rectangle_t rect; + + rect.x = interest_rect->x; + rect.y = interest_rect->y; + rect.width = interest_rect->width; + rect.height = interest_rect->width; + + if (rect.x > x1) + x1 = rect.x; + if (rect.y > y1) + y1 = rect.y; + if (rect.x + rect.width < x2) + x2 = rect.x + rect.width; + if (rect.y + rect.height < y2) + y2 = rect.y + rect.height; if (x1 >= x2 || y1 >= y2) { *image_out = NULL; @@ -376,47 +395,132 @@ _get_image_surface (cairo_xcb_surface_t *surface, image_rect->height = y2 - y1; } - imagerep = XCBGetImageReply(surface->dpy, - XCBGetImage(surface->dpy, ZPixmap, - surface->drawable, - x1, y1, - x2 - x1, y2 - y1, - AllPlanes), 0); - if(!imagerep) - return 0; + /* XXX: This should try to use the XShm extension if available */ - bpp = bits_per_pixel(surface->dpy, imagerep->depth); + if (surface->use_pixmap == 0) + { + XCBGenericError *error; + imagerep = XCBGetImageReply(surface->dpy, + XCBGetImage(surface->dpy, ZPixmap, + surface->drawable, + x1, y1, + x2 - x1, y2 - y1, + AllPlanes), &error); + + /* If we get an error, the surface must have been a window, + * so retry with the safe code path. + */ + if (error) + surface->use_pixmap = CAIRO_ASSUME_PIXMAP; + } + else + { + surface->use_pixmap--; + imagerep = NULL; + } - if (surface->visual) { - cairo_format_masks_t masks; + if (!imagerep) + { + /* XCBGetImage from a window is dangerous because it can + * produce errors if the window is unmapped or partially + * outside the screen. We could check for errors and + * retry, but to keep things simple, we just create a + * temporary pixmap + */ + XCBDRAWABLE drawable; + drawable.pixmap = XCBPIXMAPNew (surface->dpy); + XCBCreatePixmap (surface->dpy, + surface->depth, + drawable.pixmap, + surface->drawable, + x2 - x1, y2 - y1); + _cairo_xcb_surface_ensure_gc (surface); + + XCBCopyArea (surface->dpy, surface->drawable, drawable, surface->gc, + x1, y1, 0, 0, x2 - x1, y2 - y1); + + imagerep = XCBGetImageReply(surface->dpy, + XCBGetImage(surface->dpy, ZPixmap, + drawable, + x1, y1, + x2 - x1, y2 - y1, + AllPlanes), 0); + XCBFreePixmap (surface->dpy, drawable.pixmap); + + } + if (!imagerep) + return CAIRO_STATUS_NO_MEMORY; - /* XXX: Add support here for pictures with external alpha? */ + bpp = _bits_per_pixel(surface->dpy, imagerep->depth); + bytes_per_line = _bytes_per_line(surface->dpy, surface->width, bpp); + data = malloc (bytes_per_line * surface->height); + if (data == NULL) { + free (imagerep); + return CAIRO_STATUS_NO_MEMORY; + } + + memcpy (data, XCBGetImageData (imagerep), bytes_per_line * surface->height); + free (imagerep); + + /* + * Compute the pixel format masks from either an XCBVISUALTYPE or + * a XCBRenderPCTFORMINFO, failing we assume the drawable is an + * alpha-only pixmap as it could only have been created that way + * through the cairo_xlib_surface_create_for_bitmap function. + */ + if (surface->visual) { masks.bpp = bpp; masks.alpha_mask = 0; masks.red_mask = surface->visual->red_mask; masks.green_mask = surface->visual->green_mask; masks.blue_mask = surface->visual->blue_mask; - - image = _cairo_image_surface_create_with_masks (XCBGetImageData(imagerep), - &masks, - x2 - x1, - y2 - y1, - bytes_per_line(surface->dpy, surface->width, bpp)); + } else if (surface->has_format) { + masks.bpp = bpp; + masks.red_mask = surface->format.direct.red_mask << surface->format.direct.red_shift; + masks.green_mask = surface->format.direct.green_mask << surface->format.direct.green_shift; + masks.blue_mask = surface->format.direct.blue_mask << surface->format.direct.blue_shift; + masks.alpha_mask = surface->format.direct.alpha_mask << surface->format.direct.alpha_shift; } else { + masks.bpp = bpp; + masks.red_mask = 0; + masks.green_mask = 0; + masks.blue_mask = 0; + if (surface->depth < 32) + masks.alpha_mask = (1 << surface->depth) - 1; + else + masks.alpha_mask = 0xffffffff; + } + + /* + * Prefer to use a standard pixman format instead of the + * general masks case. + */ + if (_CAIRO_MASK_FORMAT (&masks, &format)) { image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (XCBGetImageData(imagerep), - surface->format, + cairo_image_surface_create_for_data (data, + format, x2 - x1, y2 - y1, - bytes_per_line(surface->dpy, surface->width, bpp)); + bytes_per_line); + } else { + /* + * XXX This can't work. We must convert the data to one of the + * supported pixman formats. Pixman needs another function + * which takes data in an arbitrary format and converts it + * to something supported by that library. + */ + image = _cairo_image_surface_create_with_masks (data, + &masks, + x2 - x1, + y2 - y1, + bytes_per_line); + } /* Let the surface take ownership of the data */ - /* XXX: Can probably come up with a cleaner API here. */ _cairo_image_surface_assume_ownership_of_data (image); - /* FIXME: imagerep can't be freed correctly, I think. must copy. :-( */ - + _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); @@ -443,8 +547,8 @@ _draw_image_surface (cairo_xcb_surface_t *surface, int bpp, data_len; _cairo_xcb_surface_ensure_gc (surface); - bpp = bits_per_pixel(surface->dpy, image->depth); - data_len = bytes_per_line(surface->dpy, image->width, bpp) * image->height; + bpp = _bits_per_pixel(surface->dpy, image->depth); + data_len = _bytes_per_line(surface->dpy, image->width, bpp) * image->height; XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, image->width, image->height, @@ -465,13 +569,8 @@ _cairo_xcb_surface_acquire_source_image (void *abstract_surfa cairo_status_t status; status = _get_image_surface (surface, NULL, &image, NULL); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_set_filter (&image->base, surface->base.filter); - cairo_surface_set_matrix (&image->base, &surface->base.matrix); - cairo_surface_set_repeat (&image->base, surface->base.repeat); - + if (status == CAIRO_STATUS_SUCCESS) *image_out = image; - } return status; } @@ -562,13 +661,13 @@ _cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, if (!surface->picture.xid) return CAIRO_STATUS_SUCCESS; - xtransform.matrix11 = _cairo_fixed_from_double (matrix->m[0][0]); - xtransform.matrix12 = _cairo_fixed_from_double (matrix->m[1][0]); - xtransform.matrix13 = _cairo_fixed_from_double (matrix->m[2][0]); + xtransform.matrix11 = _cairo_fixed_from_double (matrix->xx); + xtransform.matrix12 = _cairo_fixed_from_double (matrix->xy); + xtransform.matrix13 = _cairo_fixed_from_double (matrix->x0); - xtransform.matrix21 = _cairo_fixed_from_double (matrix->m[0][1]); - xtransform.matrix22 = _cairo_fixed_from_double (matrix->m[1][1]); - xtransform.matrix23 = _cairo_fixed_from_double (matrix->m[2][1]); + xtransform.matrix21 = _cairo_fixed_from_double (matrix->yx); + xtransform.matrix22 = _cairo_fixed_from_double (matrix->yy); + xtransform.matrix23 = _cairo_fixed_from_double (matrix->y0); xtransform.matrix31 = 0; xtransform.matrix32 = 0; @@ -685,25 +784,25 @@ _render_operator (cairo_operator_t operator) switch (operator) { case CAIRO_OPERATOR_CLEAR: return XCBRenderPictOpClear; - case CAIRO_OPERATOR_SRC: + case CAIRO_OPERATOR_SOURCE: return XCBRenderPictOpSrc; - case CAIRO_OPERATOR_DST: + case CAIRO_OPERATOR_DEST: return XCBRenderPictOpDst; case CAIRO_OPERATOR_OVER: return XCBRenderPictOpOver; - case CAIRO_OPERATOR_OVER_REVERSE: + case CAIRO_OPERATOR_DEST_OVER: return XCBRenderPictOpOverReverse; case CAIRO_OPERATOR_IN: return XCBRenderPictOpIn; - case CAIRO_OPERATOR_IN_REVERSE: + case CAIRO_OPERATOR_DEST_IN: return XCBRenderPictOpInReverse; case CAIRO_OPERATOR_OUT: return XCBRenderPictOpOut; - case CAIRO_OPERATOR_OUT_REVERSE: + case CAIRO_OPERATOR_DEST_OUT: return XCBRenderPictOpOutReverse; case CAIRO_OPERATOR_ATOP: return XCBRenderPictOpAtop; - case CAIRO_OPERATOR_ATOP_REVERSE: + case CAIRO_OPERATOR_DEST_ATOP: return XCBRenderPictOpAtopReverse; case CAIRO_OPERATOR_XOR: return XCBRenderPictOpXor; @@ -840,6 +939,7 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, cairo_int_status_t status; int render_reference_x, render_reference_y; int render_src_x, render_src_y; + XCBRenderPICTFORMINFO render_format; if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -863,13 +963,14 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, render_src_y = src_y + render_reference_y - dst_y; /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ - /* XXX: format_from_cairo is slow. should cache something. */ + /* XXX: _format_from_cairo is slow. should cache something. */ + render_format = _format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), status = _cairo_xcb_surface_set_attributes (src, &attributes); if (CAIRO_OK (status)) XCBRenderTrapezoids (dst->dpy, _render_operator (operator), src->picture, dst->picture, - format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), + render_format.id, render_src_x + attributes.x_offset, render_src_y + attributes.y_offset, num_traps, (XCBRenderTRAP *) traps); @@ -880,29 +981,31 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, } static cairo_int_status_t -_cairo_xcb_surface_copy_page (void *abstract_surface) +_cairo_xcb_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) { + /* XXX: FIXME */ return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t -_cairo_xcb_surface_show_page (void *abstract_surface) +_cairo_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) { - return CAIRO_INT_STATUS_UNSUPPORTED; -} + cairo_xcb_surface_t *surface = abstract_surface; -static cairo_int_status_t -_cairo_xcb_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - /* FIXME */ - return CAIRO_INT_STATUS_UNSUPPORTED; + rectangle->x = 0; + rectangle->y = 0; + + rectangle->width = surface->width; + rectangle->height = surface->height; + + return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_create_similar, - _cairo_xcb_surface_destroy, - _cairo_xcb_surface_pixels_per_inch, + _cairo_xcb_surface_finish, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, _cairo_xcb_surface_acquire_dest_image, @@ -911,12 +1014,27 @@ static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_composite, _cairo_xcb_surface_fill_rectangles, _cairo_xcb_surface_composite_trapezoids, - _cairo_xcb_surface_copy_page, - _cairo_xcb_surface_show_page, + NULL, /* copy_page */ + NULL, /* show_page */ _cairo_xcb_surface_set_clip_region, + _cairo_xcb_surface_get_extents, NULL /* show_glyphs */ }; +/** + * _cairo_surface_is_xcb: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_xcb_surface_t + * + * Return value: True if the surface is an xcb surface + **/ +static cairo_bool_t +_cairo_surface_is_xcb (cairo_surface_t *surface) +{ + return surface->backend == &cairo_xcb_surface_backend; +} + static void query_render_version (XCBConnection *c, cairo_xcb_surface_t *surface) { @@ -937,11 +1055,14 @@ query_render_version (XCBConnection *c, cairo_xcb_surface_t *surface) free(r); } -cairo_surface_t * -cairo_xcb_surface_create (XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format) +static cairo_surface_t * +_cairo_xcb_surface_create_internal (XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + XCBRenderPICTFORMINFO *format, + int width, + int height, + int depth) { cairo_xcb_surface_t *surface; @@ -952,27 +1073,199 @@ cairo_xcb_surface_create (XCBConnection *dpy, _cairo_surface_init (&surface->base, &cairo_xcb_surface_backend); surface->dpy = dpy; + surface->gc.xid = 0; surface->drawable = drawable; - surface->owns_pixmap = 0; + surface->owns_pixmap = FALSE; surface->visual = visual; - surface->format = format; + if (format) { + surface->format = *format; + surface->has_format = 1; + } else { + surface->format.id.xid = 0; + surface->has_format = 0; + } + surface->use_pixmap = 0; + surface->width = width; + surface->height = height; + surface->depth = depth; + + if (format) { + surface->depth = format->depth; + } else if (visual) { + XCBSCREENIter roots; + XCBDEPTHIter depths; + XCBVISUALTYPEIter visuals; + + /* This is ugly, but we have to walk over all visuals + * for the display to find the depth. + */ + roots = XCBConnSetupSuccessRepRootsIter(XCBGetSetup(surface->dpy)); + for(; roots.rem; XCBSCREENNext(&roots)) + { + depths = XCBSCREENAllowedDepthsIter(roots.data); + for(; depths.rem; XCBDEPTHNext(&depths)) + { + visuals = XCBDEPTHVisualsIter(depths.data); + for(; visuals.rem; XCBVISUALTYPENext(&visuals)) + { + if(visuals.data->visual_id.id == visual->visual_id.id) + { + surface->depth = depths.data->depth; + goto found; + } + } + } + } + found: + ; + } query_render_version(dpy, surface); + surface->picture.xid = 0; + if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { - XCBRenderPICTFORMAT fmt; - if(visual) - fmt = format_from_visual (dpy, visual->visual_id); - else - fmt = format_from_cairo (dpy, format); + XCBRenderPICTFORMAT pict_format = {0}; + XCBRenderPICTFORMINFO format_info; + surface->picture = XCBRenderPICTURENew(dpy); - XCBRenderCreatePicture (dpy, surface->picture, drawable, - fmt, 0, NULL); + + if (!format) { + if (visual) { + pict_format = format_from_visual (dpy, visual->visual_id); + } else if (depth == 1) { + format_info = _format_from_cairo (dpy, CAIRO_FORMAT_A1); + pict_format = format_info.id; + } + XCBRenderCreatePicture (dpy, surface->picture, drawable, + pict_format, 0, NULL); + } else { + XCBRenderCreatePicture (dpy, surface->picture, drawable, + format->id, 0, NULL); + } } - else - surface->picture.xid = 0; return (cairo_surface_t *) surface; } + +/** + * cairo_xcb_surface_create: + * @c: an XCB connection + * @drawable: an XCB drawable + * @visual: the visual to use for drawing to @drawable. The depth + * of the visual must match the depth of the drawable. + * Currently, only TrueColor visuals are fully supported. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an XCB surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided visual. + * + * NOTE: If @drawable is a window, then the function + * cairo_xcb_surface_set_size must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xcb_surface_create (XCBConnection *c, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + int width, + int height) +{ + return _cairo_xcb_surface_create_internal (c, drawable, + visual, NULL, + width, height, 0); +} + +/** + * cairo_xcb_surface_create_for_bitmap: + * @c: an XCB connection + * @bitmap: an XCB Pixmap (a depth-1 pixmap) + * @width: the current width of @bitmap + * @height: the current height of @bitmap + * + * Creates an XCB surface that draws to the given bitmap. + * This will be drawn to as a CAIRO_FORMAT_A1 object. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xcb_surface_create_for_bitmap (XCBConnection *c, + XCBPIXMAP bitmap, + int width, + int height) +{ + XCBDRAWABLE drawable; + drawable.pixmap = bitmap; + return _cairo_xcb_surface_create_internal (c, drawable, + NULL, NULL, + width, height, 1); +} + +/** + * cairo_xcb_surface_create_with_xrender_format: + * @c: an XCB connection + * @drawable: an XCB drawable + * @format: the picture format to use for drawing to @drawable. The + * depth of @format mush match the depth of the drawable. + * @width: the current width of @drawable + * @height: the current height of @drawable + * + * Creates an XCB surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided picture format. + * + * NOTE: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface. + **/ +cairo_surface_t * +cairo_xcb_surface_create_with_xrender_format (XCBConnection *c, + XCBDRAWABLE drawable, + XCBRenderPICTFORMINFO *format, + int width, + int height) +{ + return _cairo_xcb_surface_create_internal (c, drawable, + NULL, format, + width, height, 0); +} + +/** + * cairo_xcb_surface_set_size: + * @surface: a #cairo_surface_t for the XCB backend + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new size of the XCB drawable underlying the + * surface. For a surface created for a window (rather than a pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) + * + * A pixmap can never change size, so it is never necessary to call + * this function on a surface created for a pixmap. + **/ +void +cairo_xcb_surface_set_size (cairo_surface_t *surface, + int width, + int height) +{ + cairo_xcb_surface_t *xcb_surface = (cairo_xcb_surface_t *)surface; + + /* XXX: How do we want to handle this error case? */ + if (! _cairo_surface_is_xcb (surface)) + return; + + xcb_surface->width = width; + xcb_surface->height = height; +} + diff --git a/src/cairo_fixed.c b/src/cairo-xcb-xrender.h index a4faa1708..ef4baa991 100644 --- a/src/cairo_fixed.c +++ b/src/cairo-xcb-xrender.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2003 University of Southern California + * 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 @@ -34,58 +34,29 @@ * Carl D. Worth <cworth@cworth.org> */ -#include "cairoint.h" +#ifndef CAIRO_XCB_XRENDER_H +#define CAIRO_XCB_XRENDER_H -cairo_fixed_t -_cairo_fixed_from_int (int i) -{ - return i << 16; -} +#include <cairo.h> -cairo_fixed_t -_cairo_fixed_from_double (double d) -{ - return (cairo_fixed_t) (d * 65536); -} +#if CAIRO_HAS_XCB_SURFACE -cairo_fixed_t -_cairo_fixed_from_26_6 (uint32_t i) -{ - return i << 10; -} +#include <X11/XCB/xcb.h> +#include <X11/XCB/render.h> -double -_cairo_fixed_to_double (cairo_fixed_t f) -{ - return ((double) f) / 65536.0; -} +CAIRO_BEGIN_DECLS -int -_cairo_fixed_is_integer (cairo_fixed_t f) -{ - return (f & 0xFFFF) == 0; -} +cairo_surface_t * +cairo_xcb_surface_create_with_xrender_format (XCBConnection *c, + XCBDRAWABLE drawable, + XCBRenderPICTFORMINFO *format, + int width, + int height); -int -_cairo_fixed_integer_part (cairo_fixed_t f) -{ - return f >> 16; -} +CAIRO_END_DECLS -int -_cairo_fixed_integer_floor (cairo_fixed_t f) -{ - if (f >= 0) - return f >> 16; - else - return -((-f - 1) >> 16) - 1; -} +#else /* CAIRO_HAS_XCB_SURFACE */ +# error Cairo was not compiled with support for the xcb backend +#endif /* CAIRO_HAS_XCB_SURFACE */ -int -_cairo_fixed_integer_ceil (cairo_fixed_t f) -{ - if (f >= 0) - return ((f - 1)>>16) + 1; - else - return - (-f >> 16); -} +#endif /* CAIRO_XCB_XRENDER_H */ diff --git a/src/cairo-xcb.h b/src/cairo-xcb.h index a5c65f441..57a7aebd1 100644 --- a/src/cairo-xcb.h +++ b/src/cairo-xcb.h @@ -39,21 +39,34 @@ #include <cairo.h> -#ifdef CAIRO_HAS_XCB_SURFACE +#if CAIRO_HAS_XCB_SURFACE #include <X11/XCB/xcb.h> -#include <X11/XCB/render.h> CAIRO_BEGIN_DECLS +cairo_surface_t * +cairo_xcb_surface_create (XCBConnection *c, + XCBDRAWABLE pixmap, + XCBVISUALTYPE *visual, + int width, + int height); + +cairo_surface_t * +cairo_xcb_surface_create_for_bitmap (XCBConnection *c, + XCBPIXMAP bitmap, + int width, + int height); + void -cairo_set_target_xcb (cairo_t *cr, - XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format); +cairo_xcb_surface_set_size (cairo_surface_t *surface, + int width, + int height); CAIRO_END_DECLS +#else /* CAIRO_HAS_XCB_SURFACE */ +# error Cairo was not compiled with support for the xcb backend #endif /* CAIRO_HAS_XCB_SURFACE */ + #endif /* CAIRO_XCB_H */ diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 3eaef57e5..41fb00c44 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -36,66 +36,47 @@ #include "cairoint.h" #include "cairo-xlib.h" +#include "cairo-xlib-xrender.h" +#include "cairo-xlib-test.h" +#include <X11/extensions/Xrender.h> -/** - * cairo_set_target_drawable: - * @cr: a #cairo_t - * @dpy: an X display - * @drawable: a window or pixmap on the default screen of @dpy - * - * Directs output for a #cairo_t to an Xlib drawable. @drawable must - * be a Window or Pixmap on the default screen of @dpy using the - * default colormap and visual. Using this function is slow because - * the function must retrieve information about @drawable from the X - * server. - - * The combination of cairo_xlib_surface_create() and - * cairo_set_target_surface() is somewhat more flexible, although - * it still is slow. - **/ -void -cairo_set_target_drawable (cairo_t *cr, - Display *dpy, - Drawable drawable) -{ - cairo_surface_t *surface; +/* Xlib doesn't define a typedef, so define one ourselves */ +typedef int (*cairo_xlib_error_func_t) (Display *display, + XErrorEvent *event); - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; +typedef struct _cairo_xlib_surface cairo_xlib_surface_t; - surface = cairo_xlib_surface_create (dpy, drawable, - DefaultVisual (dpy, DefaultScreen (dpy)), - 0, - DefaultColormap (dpy, DefaultScreen (dpy))); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } +static void _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface); - cairo_set_target_surface (cr, surface); +/* + * Instead of taking two round trips for each blending request, + * assume that if a particular drawable fails GetImage that it will + * fail for a "while"; use temporary pixmaps to avoid the errors + */ - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} +#define CAIRO_ASSUME_PIXMAP 20 -typedef struct _cairo_xlib_surface { +struct _cairo_xlib_surface { cairo_surface_t base; Display *dpy; GC gc; Drawable drawable; - int owns_pixmap; + cairo_bool_t owns_pixmap; Visual *visual; - cairo_format_t format; + + int use_pixmap; int render_major; int render_minor; int width; int height; + int depth; Picture picture; -} cairo_xlib_surface_t; + XRenderPictFormat *format; +}; #define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ (((surface)->render_major > major) || \ @@ -120,6 +101,25 @@ typedef struct _cairo_xlib_surface { #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +static cairo_bool_t cairo_xlib_render_disabled = FALSE; + +/** + * cairo_test_xlib_disable_render: + * + * Disables the use of the RENDER extension. + * + * <note> + * This function is <emphasis>only</emphasis> intended for internal + * testing use within the cairo distribution. It is not installed in + * any public header file. + * </note> + **/ +void +cairo_test_xlib_disable_render (void) +{ + cairo_xlib_render_disabled = TRUE; +} + static int _CAIRO_FORMAT_DEPTH (cairo_format_t format) { @@ -136,15 +136,23 @@ _CAIRO_FORMAT_DEPTH (cairo_format_t format) } } -static cairo_surface_t * -_cairo_xlib_surface_create_with_size (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap, - int width, - int height); - +static XRenderPictFormat * +_CAIRO_FORMAT_XRENDER_FORMAT(Display *dpy, cairo_format_t format) +{ + int pict_format; + switch (format) { + case CAIRO_FORMAT_A1: + pict_format = PictStandardA1; break; + case CAIRO_FORMAT_A8: + pict_format = PictStandardA8; break; + case CAIRO_FORMAT_RGB24: + pict_format = PictStandardRGB24; break; + case CAIRO_FORMAT_ARGB32: + default: + pict_format = PictStandardARGB32; break; + } + return XRenderFindStandardFormat (dpy, pict_format); +} static cairo_surface_t * _cairo_xlib_surface_create_similar (void *abstract_src, @@ -158,6 +166,9 @@ _cairo_xlib_surface_create_similar (void *abstract_src, int scr; Pixmap pix; cairo_xlib_surface_t *surface; + int depth = _CAIRO_FORMAT_DEPTH (format); + XRenderPictFormat *xrender_format = _CAIRO_FORMAT_XRENDER_FORMAT (dpy, + format); /* As a good first approximation, if the display doesn't have COMPOSITE, * we're better off using image surfaces for all temporary operations @@ -170,22 +181,20 @@ _cairo_xlib_surface_create_similar (void *abstract_src, pix = XCreatePixmap (dpy, DefaultRootWindow (dpy), width <= 0 ? 1 : width, height <= 0 ? 1 : height, - _CAIRO_FORMAT_DEPTH (format)); + depth); surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_with_size (dpy, pix, NULL, format, - DefaultColormap (dpy, scr), - width, height); - surface->owns_pixmap = 1; - - surface->width = width; - surface->height = height; + cairo_xlib_surface_create_with_xrender_format (dpy, pix, + xrender_format, + width, height); + + surface->owns_pixmap = TRUE; return &surface->base; } -static void -_cairo_xlib_surface_destroy (void *abstract_surface) +static cairo_status_t +_cairo_xlib_surface_finish (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; if (surface->picture) @@ -197,16 +206,56 @@ _cairo_xlib_surface_destroy (void *abstract_surface) if (surface->gc) XFreeGC (surface->dpy, surface->gc); - surface->dpy = 0; + surface->dpy = NULL; + + return CAIRO_STATUS_SUCCESS; +} - free (surface); +static int +_noop_error_handler (Display *display, + XErrorEvent *event) +{ + return False; /* return value is ignored */ } -static double -_cairo_xlib_surface_pixels_per_inch (void *abstract_surface) +static cairo_bool_t +_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) { - /* XXX: We should really get this value from somewhere like Xft.dpy */ - return 96.0; + switch (masks->bpp) { + case 32: + if (masks->alpha_mask == 0xff000000 && + masks->red_mask == 0x00ff0000 && + masks->green_mask == 0x0000ff00 && + masks->blue_mask == 0x000000ff) + { + *format = CAIRO_FORMAT_ARGB32; + return True; + } + if (masks->alpha_mask == 0x00000000 && + masks->red_mask == 0x00ff0000 && + masks->green_mask == 0x0000ff00 && + masks->blue_mask == 0x000000ff) + { + *format = CAIRO_FORMAT_RGB24; + return True; + } + break; + case 8: + if (masks->alpha_mask == 0xff) + { + *format = CAIRO_FORMAT_A8; + return True; + } + break; + case 1: + if (masks->alpha_mask == 0x1) + { + *format = CAIRO_FORMAT_A1; + return True; + } + break; + } + return False; } static cairo_status_t @@ -217,15 +266,9 @@ _get_image_surface (cairo_xlib_surface_t *surface, { cairo_image_surface_t *image; XImage *ximage; - Window root_ignore; - int x_ignore, y_ignore, bwidth_ignore, depth_ignore; int x1, y1, x2, y2; - - XGetGeometry (surface->dpy, - surface->drawable, - &root_ignore, &x_ignore, &y_ignore, - &surface->width, &surface->height, - &bwidth_ignore, &depth_ignore); + cairo_format_masks_t masks; + cairo_format_t format; x1 = 0; y1 = 0; @@ -233,14 +276,21 @@ _get_image_surface (cairo_xlib_surface_t *surface, y2 = surface->height; if (interest_rect) { - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if (interest_rect->x + interest_rect->width < x2) - x2 = interest_rect->x + interest_rect->width; - if (interest_rect->y + interest_rect->height < y2) - y2 = interest_rect->y + interest_rect->height; + cairo_rectangle_t rect; + + rect.x = interest_rect->x; + rect.y = interest_rect->y; + rect.width = interest_rect->width; + rect.height = interest_rect->width; + + if (rect.x > x1) + x1 = rect.x; + if (rect.y > y1) + y1 = rect.y; + if (rect.x + rect.width < x2) + x2 = rect.x + rect.width; + if (rect.y + rect.height < y2) + y2 = rect.y + rect.height; if (x1 >= x2 || y1 >= y2) { *image_out = NULL; @@ -255,36 +305,118 @@ _get_image_surface (cairo_xlib_surface_t *surface, image_rect->height = y2 - y1; } - /* XXX: This should try to use the XShm extension if availible */ - ximage = XGetImage (surface->dpy, - surface->drawable, - x1, y1, - x2 - x1, y2 - y1, - AllPlanes, ZPixmap); + /* XXX: This should try to use the XShm extension if available */ - if (surface->visual) { - cairo_format_masks_t masks; + if (surface->use_pixmap == 0) + { + cairo_xlib_error_func_t old_handler; - /* XXX: Add support here for pictures with external alpha? */ + old_handler = XSetErrorHandler (_noop_error_handler); + + ximage = XGetImage (surface->dpy, + surface->drawable, + x1, y1, + x2 - x1, y2 - y1, + AllPlanes, ZPixmap); + XSetErrorHandler (old_handler); + + /* If we get an error, the surface must have been a window, + * so retry with the safe code path. + */ + if (!ximage) + surface->use_pixmap = CAIRO_ASSUME_PIXMAP; + } + else + { + surface->use_pixmap--; + ximage = 0; + } + + if (!ximage) + { + + /* XGetImage from a window is dangerous because it can + * produce errors if the window is unmapped or partially + * outside the screen. We could check for errors and + * retry, but to keep things simple, we just create a + * temporary pixmap + */ + Pixmap pixmap = XCreatePixmap (surface->dpy, + surface->drawable, + x2 - x1, y2 - y1, + surface->depth); + _cairo_xlib_surface_ensure_gc (surface); + + XCopyArea (surface->dpy, surface->drawable, pixmap, surface->gc, + x1, y1, x2 - x1, y2 - y1, 0, 0); + + ximage = XGetImage (surface->dpy, + pixmap, + 0, 0, + x2 - x1, y2 - y1, + AllPlanes, ZPixmap); + + XFreePixmap (surface->dpy, pixmap); + } + if (!ximage) + return CAIRO_STATUS_NO_MEMORY; + + /* + * Compute the pixel format masks from either a visual or a + * XRenderFormat, failing we assume the drawable is an + * alpha-only pixmap as it could only have been created + * that way through the cairo_xlib_surface_create_for_bitmap + * function. + */ + if (surface->visual) { masks.bpp = ximage->bits_per_pixel; masks.alpha_mask = 0; masks.red_mask = surface->visual->red_mask; masks.green_mask = surface->visual->green_mask; masks.blue_mask = surface->visual->blue_mask; + } else if (surface->format) { + masks.bpp = ximage->bits_per_pixel; + masks.red_mask = surface->format->direct.redMask << surface->format->direct.red; + masks.green_mask = surface->format->direct.greenMask << surface->format->direct.green; + masks.blue_mask = surface->format->direct.blueMask << surface->format->direct.blue; + masks.alpha_mask = surface->format->direct.alphaMask << surface->format->direct.alpha; + } else { + masks.bpp = ximage->bits_per_pixel; + masks.red_mask = 0; + masks.green_mask = 0; + masks.blue_mask = 0; + if (surface->depth < 32) + masks.alpha_mask = (1 << surface->depth) - 1; + else + masks.alpha_mask = 0xffffffff; + } - image = _cairo_image_surface_create_with_masks (ximage->data, + /* + * Prefer to use a standard pixman format instead of the + * general masks case. + */ + if (_CAIRO_MASK_FORMAT (&masks, &format)) + { + image = (cairo_image_surface_t *) cairo_image_surface_create_for_data ((unsigned char *) ximage->data, + format, + ximage->width, + ximage->height, + ximage->bytes_per_line); + } + else + { + /* + * XXX This can't work. We must convert the data to one of the + * supported pixman formats. Pixman needs another function + * which takes data in an arbitrary format and converts it + * to something supported by that library. + */ + image = _cairo_image_surface_create_with_masks ((unsigned char *) ximage->data, &masks, ximage->width, ximage->height, ximage->bytes_per_line); - } else { - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (ximage->data, - surface->format, - ximage->width, - ximage->height, - ximage->bytes_per_line); } /* Let the surface take ownership of the data */ @@ -303,10 +435,14 @@ _get_image_surface (cairo_xlib_surface_t *surface, static void _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) { + XGCValues gcv; + if (surface->gc) return; - surface->gc = XCreateGC (surface->dpy, surface->drawable, 0, NULL); + gcv.graphics_exposures = False; + surface->gc = XCreateGC (surface->dpy, surface->drawable, + GCGraphicsExposures, &gcv); } static cairo_status_t @@ -318,6 +454,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, XImage *ximage; unsigned bitmap_pad; + /* XXX this is wrong */ if (image->depth > 16) bitmap_pad = 32; else if (image->depth > 8) @@ -330,7 +467,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, image->depth, ZPixmap, 0, - image->data, + (char *) image->data, image->width, image->height, bitmap_pad, @@ -362,13 +499,8 @@ _cairo_xlib_surface_acquire_source_image (void *abstract_surf cairo_status_t status; status = _get_image_surface (surface, NULL, &image, NULL); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_set_filter (&image->base, surface->base.filter); - cairo_surface_set_matrix (&image->base, &surface->base.matrix); - cairo_surface_set_repeat (&image->base, surface->base.repeat); - + if (status == CAIRO_STATUS_SUCCESS) *image_out = image; - } return status; } @@ -459,13 +591,13 @@ _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, if (!surface->picture) return CAIRO_STATUS_SUCCESS; - xtransform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - xtransform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - xtransform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + xtransform.matrix[0][0] = _cairo_fixed_from_double (matrix->xx); + xtransform.matrix[0][1] = _cairo_fixed_from_double (matrix->xy); + xtransform.matrix[0][2] = _cairo_fixed_from_double (matrix->x0); - xtransform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - xtransform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - xtransform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); + xtransform.matrix[1][0] = _cairo_fixed_from_double (matrix->yx); + xtransform.matrix[1][1] = _cairo_fixed_from_double (matrix->yy); + xtransform.matrix[1][2] = _cairo_fixed_from_double (matrix->y0); xtransform.matrix[2][0] = 0; xtransform.matrix[2][1] = 0; @@ -585,26 +717,29 @@ _render_operator (cairo_operator_t operator) switch (operator) { case CAIRO_OPERATOR_CLEAR: return PictOpClear; - case CAIRO_OPERATOR_SRC: + + case CAIRO_OPERATOR_SOURCE: return PictOpSrc; - case CAIRO_OPERATOR_DST: - return PictOpDst; case CAIRO_OPERATOR_OVER: return PictOpOver; - case CAIRO_OPERATOR_OVER_REVERSE: - return PictOpOverReverse; case CAIRO_OPERATOR_IN: return PictOpIn; - case CAIRO_OPERATOR_IN_REVERSE: - return PictOpInReverse; case CAIRO_OPERATOR_OUT: return PictOpOut; - case CAIRO_OPERATOR_OUT_REVERSE: - return PictOpOutReverse; case CAIRO_OPERATOR_ATOP: return PictOpAtop; - case CAIRO_OPERATOR_ATOP_REVERSE: + + case CAIRO_OPERATOR_DEST: + return PictOpDst; + case CAIRO_OPERATOR_DEST_OVER: + return PictOpOverReverse; + case CAIRO_OPERATOR_DEST_IN: + return PictOpInReverse; + case CAIRO_OPERATOR_DEST_OUT: + return PictOpOutReverse; + case CAIRO_OPERATOR_DEST_ATOP: return PictOpAtopReverse; + case CAIRO_OPERATOR_XOR: return PictOpXor; case CAIRO_OPERATOR_ADD: @@ -651,10 +786,8 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, return status; status = _cairo_xlib_surface_set_attributes (src, &src_attr); - if (CAIRO_OK (status)) - { - if (mask) - { + if (CAIRO_OK (status)) { + if (mask) { status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); if (CAIRO_OK (status)) XRenderComposite (dst->dpy, @@ -668,9 +801,7 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, mask_y + mask_attr.y_offset, dst_x, dst_y, width, height); - } - else - { + } else { XRenderComposite (dst->dpy, _render_operator (operator), src->picture, @@ -777,83 +908,75 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, } static cairo_int_status_t -_cairo_xlib_surface_copy_page (void *abstract_surface) +_cairo_xlib_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) { - return CAIRO_INT_STATUS_UNSUPPORTED; -} + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; -static cairo_int_status_t -_cairo_xlib_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; + if (region == NULL) { + if (surface->gc) + XSetClipMask (surface->dpy, surface->gc, None); + + if (surface->picture) { + XRenderPictureAttributes pa; + pa.clip_mask = None; + XRenderChangePicture (surface->dpy, surface->picture, + CPClipMask, &pa); + } + } else { + pixman_box16_t *boxes; + XRectangle *rects = NULL; + int n_boxes, i; + + n_boxes = pixman_region_num_rects (region); + if (n_boxes > 0) { + rects = malloc (sizeof(XRectangle) * n_boxes); + if (rects == NULL) + return CAIRO_STATUS_NO_MEMORY; + } else { + rects = NULL; + } + + boxes = pixman_region_rects (region); + + for (i = 0; i < n_boxes; i++) { + rects[i].x = boxes[i].x1; + rects[i].y = boxes[i].y1; + rects[i].width = boxes[i].x2 - boxes[i].x1; + rects[i].height = boxes[i].y2 - boxes[i].y1; + } + + if (surface->gc) + XSetClipRectangles(surface->dpy, surface->gc, + 0, 0, rects, n_boxes, YXSorted); + if (surface->picture) + XRenderSetPictureClipRectangles (surface->dpy, surface->picture, + 0, 0, rects, n_boxes); + + if (rects) + free (rects); + } + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_xlib_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) +_cairo_xlib_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) { + cairo_xlib_surface_t *surface = abstract_surface; - Region xregion; - XRectangle xr; - XRectangle *rects = NULL; - XGCValues gc_values; - pixman_box16_t *box; - cairo_xlib_surface_t *surf; - int n, m; - - surf = (cairo_xlib_surface_t *) abstract_surface; + rectangle->x = 0; + rectangle->y = 0; - if (region == NULL) { - /* NULL region == reset the clip */ - xregion = XCreateRegion(); - xr.x = 0; - xr.y = 0; - xr.width = surf->width; - xr.height = surf->height; - XUnionRectWithRegion (&xr, xregion, xregion); - rects = malloc(sizeof(XRectangle)); - if (rects == NULL) - return CAIRO_STATUS_NO_MEMORY; - rects[0] = xr; - m = 1; + rectangle->width = surface->width; + rectangle->height = surface->height; - } else { - n = pixman_region_num_rects (region); - /* XXX: Are we sure these are the semantics we want for an - * empty, (not null) region? */ - if (n == 0) - return CAIRO_STATUS_SUCCESS; - rects = malloc(sizeof(XRectangle) * n); - if (rects == NULL) - return CAIRO_STATUS_NO_MEMORY; - box = pixman_region_rects (region); - xregion = XCreateRegion(); - - m = n; - for (; n > 0; --n, ++box) { - xr.x = (short) box->x1; - xr.y = (short) box->y1; - xr.width = (unsigned short) (box->x2 - box->x1); - xr.height = (unsigned short) (box->y2 - box->y1); - rects[n-1] = xr; - XUnionRectWithRegion (&xr, xregion, xregion); - } - } - - _cairo_xlib_surface_ensure_gc (surf); - XGetGCValues(surf->dpy, surf->gc, GCGraphicsExposures, &gc_values); - XSetGraphicsExposures(surf->dpy, surf->gc, False); - XSetClipRectangles(surf->dpy, surf->gc, 0, 0, rects, m, Unsorted); - free(rects); - if (surf->picture) - XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); - XDestroyRegion(xregion); - XSetGraphicsExposures(surf->dpy, surf->gc, gc_values.graphics_exposures); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_font_t *font, +static cairo_int_status_t +_cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, cairo_pattern_t *pattern, void *abstract_surface, @@ -868,8 +991,7 @@ _cairo_xlib_surface_show_glyphs (cairo_font_t *font, static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, - _cairo_xlib_surface_destroy, - _cairo_xlib_surface_pixels_per_inch, + _cairo_xlib_surface_finish, _cairo_xlib_surface_acquire_source_image, _cairo_xlib_surface_release_source_image, _cairo_xlib_surface_acquire_dest_image, @@ -878,23 +1000,37 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_composite, _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, - _cairo_xlib_surface_copy_page, - _cairo_xlib_surface_show_page, + NULL, /* copy_page */ + NULL, /* show_page */ _cairo_xlib_surface_set_clip_region, + _cairo_xlib_surface_get_extents, _cairo_xlib_surface_show_glyphs }; +/** + * _cairo_surface_is_xlib: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_xlib_surface_t + * + * Return value: True if the surface is an xlib surface + **/ +static cairo_bool_t +_cairo_surface_is_xlib (cairo_surface_t *surface) +{ + return surface->backend == &cairo_xlib_surface_backend; +} + static cairo_surface_t * -_cairo_xlib_surface_create_with_size (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap, - int width, - int height) +_cairo_xlib_surface_create_internal (Display *dpy, + Drawable drawable, + Visual *visual, + XRenderPictFormat *format, + int width, + int height, + int depth) { cairo_xlib_surface_t *surface; - int render_standard; surface = malloc (sizeof (cairo_xlib_surface_t)); if (surface == NULL) @@ -902,76 +1038,180 @@ _cairo_xlib_surface_create_with_size (Display *dpy, _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend); - surface->visual = visual; - surface->format = format; - surface->dpy = dpy; - surface->gc = 0; + surface->gc = NULL; surface->drawable = drawable; - surface->owns_pixmap = 0; + surface->owns_pixmap = FALSE; surface->visual = visual; + surface->format = format; + surface->use_pixmap = 0; surface->width = width; surface->height = height; + surface->depth = depth; - if (! XRenderQueryVersion (dpy, &surface->render_major, &surface->render_minor)) { + if (format) { + surface->depth = format->depth; + } else if (visual) { + int i, j, k; + + /* This is ugly, but we have to walk over all visuals + * for the display to find the depth. + */ + for (i = 0; i < ScreenCount (dpy); i++) { + Screen *screen = ScreenOfDisplay (dpy, i); + for (j = 0; j < screen->ndepths; j++) { + Depth *depth = &screen->depths[j]; + for (k = 0; k < depth->nvisuals; k++) { + if (&depth->visuals[k] == visual) { + surface->depth = depth->depth; + goto found; + } + } + } + } + found: + ; + } + + if (cairo_xlib_render_disabled || + ! XRenderQueryVersion (dpy, &surface->render_major, &surface->render_minor)) { surface->render_major = -1; surface->render_minor = -1; } - switch (format) { - case CAIRO_FORMAT_A1: - render_standard = PictStandardA1; - break; - case CAIRO_FORMAT_A8: - render_standard = PictStandardA8; - break; - case CAIRO_FORMAT_RGB24: - render_standard = PictStandardRGB24; - break; - case CAIRO_FORMAT_ARGB32: - default: - render_standard = PictStandardARGB32; - break; - } + surface->picture = None; - /* XXX: I'm currently ignoring the colormap. Is that bad? */ - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) - surface->picture = XRenderCreatePicture (dpy, drawable, - visual ? - XRenderFindVisualFormat (dpy, visual) : - XRenderFindStandardFormat (dpy, render_standard), - 0, NULL); - else - surface->picture = 0; + if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { + + if (!format) { + if (visual) { + format = XRenderFindVisualFormat (dpy, visual); + } else if (depth == 1) + format = XRenderFindStandardFormat (dpy, PictStandardA1); + } + + if (format) + surface->picture = XRenderCreatePicture (dpy, drawable, + format, 0, NULL); + } return (cairo_surface_t *) surface; } +/** + * cairo_xlib_surface_create: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @visual: the visual to use for drawing to @drawable. The depth + * of the visual must match the depth of the drawable. + * Currently, only TrueColor visuals are fully supported. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an Xlib surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided visual. + * + * NOTE: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + **/ cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap) +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) { - Window window_ignore; - unsigned int int_ignore; - unsigned int width, height; + return _cairo_xlib_surface_create_internal (dpy, drawable, + visual, NULL, width, height, 0); +} - /* XXX: This call is a round-trip. We probably want to instead (or - * also?) export a version that accepts width/height. Then, we'll - * likely also need a resize function too. - */ - XGetGeometry(dpy, drawable, - &window_ignore, &int_ignore, &int_ignore, - &width, &height, - &int_ignore, &int_ignore); +/** + * cairo_xlib_surface_create_for_bitmap: + * @dpy: an X Display + * @bitmap: an X Drawable, (a depth-1 Pixmap) + * @width: the current width of @bitmap. + * @height: the current height of @bitmap. + * + * Creates an Xlib surface that draws to the given bitmap. + * This will be drawn to as a CAIRO_FORMAT_A1 object. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + int width, + int height) +{ + return _cairo_xlib_surface_create_internal (dpy, bitmap, + NULL, NULL, width, height, 1); +} + +/** + * cairo_xlib_surface_create_with_xrender_format: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @format: the picture format to use for drawing to @drawable. The depth + * of @format must match the depth of the drawable. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an Xlib surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided picture format. + * + * NOTE: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + XRenderPictFormat *format, + int width, + int height) +{ + return _cairo_xlib_surface_create_internal (dpy, drawable, + NULL, format, width, height, 0); +} - return _cairo_xlib_surface_create_with_size (dpy, drawable, visual, format, - colormap, width, height); +/** + * cairo_xlib_surface_set_size: + * @surface: a #cairo_surface_t for the XLib backend + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new size of the X Drawable underlying the + * surface. For a surface created for a Window (rather than a Pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) + * + * A Pixmap can never change size, so it is never necessary to call + * this function on a surface created for a Pixmap. + **/ +void +cairo_xlib_surface_set_size (cairo_surface_t *surface, + int width, + int height) +{ + cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *)surface; + + /* XXX: How do we want to handle this error case? */ + if (! _cairo_surface_is_xlib (surface)) + return; + + xlib_surface->width = width; + xlib_surface->height = height; } -DEPRECATE (cairo_surface_create_for_drawable, cairo_xlib_surface_create); /* RENDER glyphset cache code */ @@ -1079,7 +1319,7 @@ _xlib_glyphset_cache_create_entry (void *cache, XRenderAddGlyphs (g->display, g->glyphset, &(v->glyph), &(v->info), 1, - im->image ? im->image->data : NULL, + im->image ? (char *) im->image->data : NULL, im->image ? v->info.height * v->info.width : 0); v->key.base.memory = im->image ? im->image->width * im->image->stride : 0; @@ -1186,7 +1426,7 @@ _get_glyphset_cache (Display *d) #define N_STACK_BUF 1024 static cairo_status_t -_cairo_xlib_surface_show_glyphs32 (cairo_font_t *font, +_cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1263,7 +1503,7 @@ _cairo_xlib_surface_show_glyphs32 (cairo_font_t *font, static cairo_status_t -_cairo_xlib_surface_show_glyphs16 (cairo_font_t *font, +_cairo_xlib_surface_show_glyphs16 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1339,7 +1579,7 @@ _cairo_xlib_surface_show_glyphs16 (cairo_font_t *font, } static cairo_status_t -_cairo_xlib_surface_show_glyphs8 (cairo_font_t *font, +_cairo_xlib_surface_show_glyphs8 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1415,8 +1655,8 @@ _cairo_xlib_surface_show_glyphs8 (cairo_font_t *font, } -static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_font_t *font, +static cairo_int_status_t +_cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, cairo_pattern_t *pattern, void *abstract_surface, @@ -1471,7 +1711,7 @@ _cairo_xlib_surface_show_glyphs (cairo_font_t *font, /* Work out the index size to use. */ elt_size = 8; - _cairo_font_get_glyph_cache_key (font, &key); + _cairo_scaled_font_get_glyph_cache_key (scaled_font, &key); for (i = 0; i < num_glyphs; ++i) { key.index = glyphs[i].index; @@ -1499,17 +1739,17 @@ _cairo_xlib_surface_show_glyphs (cairo_font_t *font, /* Call the appropriate sub-function. */ if (elt_size == 8) - status = _cairo_xlib_surface_show_glyphs8 (font, operator, g, &key, src, self, + status = _cairo_xlib_surface_show_glyphs8 (scaled_font, operator, g, &key, src, self, source_x + attributes.x_offset, source_y + attributes.y_offset, glyphs, entries, num_glyphs); else if (elt_size == 16) - status = _cairo_xlib_surface_show_glyphs16 (font, operator, g, &key, src, self, + status = _cairo_xlib_surface_show_glyphs16 (scaled_font, operator, g, &key, src, self, source_x + attributes.x_offset, source_y + attributes.y_offset, glyphs, entries, num_glyphs); else - status = _cairo_xlib_surface_show_glyphs32 (font, operator, g, &key, src, self, + status = _cairo_xlib_surface_show_glyphs32 (scaled_font, operator, g, &key, src, self, source_x + attributes.x_offset, source_y + attributes.y_offset, glyphs, entries, num_glyphs); diff --git a/src/cairo-png.h b/src/cairo-xlib-test.h index 3e86210b0..44b986c7f 100644 --- a/src/cairo-png.h +++ b/src/cairo-xlib-test.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -34,31 +34,22 @@ * Carl D. Worth <cworth@cworth.org> */ -#ifndef CAIRO_PNG_H -#define CAIRO_PNG_H +#ifndef CAIRO_XLIB_TEST_H +#define CAIRO_XLIB_TEST_H #include <cairo.h> -#ifdef CAIRO_HAS_PNG_SURFACE +#if CAIRO_HAS_XLIB_SURFACE -#include <stdio.h> +#include <cairo-xlib.h> CAIRO_BEGIN_DECLS void -cairo_set_target_png (cairo_t *cr, - FILE *file, - cairo_format_t format, - int width, - int height); - -cairo_surface_t * -cairo_png_surface_create (FILE *file, - cairo_format_t format, - int width, - int height); +cairo_test_xlib_disable_render (void); CAIRO_END_DECLS -#endif /* CAIRO_HAS_PNG_SURFACE */ -#endif /* CAIRO_PNG_H */ +#endif /* CAIRO_HAS_XLIB_SURFACE */ +#endif /* CAIRO_XLIB_H */ + diff --git a/src/cairo_color.c b/src/cairo-xlib-xrender.h index f203d96cc..08a8624e2 100644 --- a/src/cairo_color.c +++ b/src/cairo-xlib-xrender.h @@ -34,64 +34,29 @@ * Carl D. Worth <cworth@cworth.org> */ -#include "cairoint.h" +#ifndef CAIRO_XLIB_XRENDER_H +#define CAIRO_XLIB_XRENDER_H -static cairo_color_t const CAIRO_COLOR_WHITE = { - 1.0, 1.0, 1.0, 1.0, - 0xffff, 0xffff, 0xffff, 0xffff -}; +#include <cairo.h> -static void -_cairo_color_compute_shorts (cairo_color_t *color); +#if CAIRO_HAS_XLIB_SURFACE -void -_cairo_color_init (cairo_color_t *color) -{ - *color = CAIRO_COLOR_WHITE; -} +#include <X11/extensions/Xrender.h> -void -_cairo_color_fini (cairo_color_t *color) -{ - /* Nothing to do here */ -} +CAIRO_BEGIN_DECLS -void -_cairo_color_set_rgb (cairo_color_t *color, double red, double green, double blue) -{ - color->red = red; - color->green = green; - color->blue = blue; +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + XRenderPictFormat *format, + int width, + int height); - _cairo_color_compute_shorts (color); -} -void -_cairo_color_get_rgb (const cairo_color_t *color, - double *red, double *green, double *blue) -{ - if (red) - *red = color->red; - if (green) - *green = color->green; - if (blue) - *blue = color->blue; -} +CAIRO_END_DECLS -void -_cairo_color_set_alpha (cairo_color_t *color, double alpha) -{ - color->alpha = alpha; - - _cairo_color_compute_shorts (color); -} - -static void -_cairo_color_compute_shorts (cairo_color_t *color) -{ - color->red_short = (color->red * color->alpha) * 0xffff; - color->green_short = (color->green * color->alpha) * 0xffff; - color->blue_short = (color->blue * color->alpha) * 0xffff; - color->alpha_short = color->alpha * 0xffff; -} +#else /* CAIRO_HAS_XLIB_SURFACE */ +# error Cairo was not compiled with support for the xlib backend +#endif /* CAIRO_HAS_XLIB_SURFACE */ +#endif /* CAIRO_XLIB_XRENDER_H */ diff --git a/src/cairo-xlib.h b/src/cairo-xlib.h index 18db7b114..3fe285fdb 100644 --- a/src/cairo-xlib.h +++ b/src/cairo-xlib.h @@ -39,38 +39,35 @@ #include <cairo.h> -#ifdef CAIRO_HAS_XLIB_SURFACE +#if CAIRO_HAS_XLIB_SURFACE -#include <X11/extensions/Xrender.h> +#include <X11/Xlib.h> CAIRO_BEGIN_DECLS -/* XXX: This shold be renamed to cairo_set_target_xlib to match the - * other backends */ -void -cairo_set_target_drawable (cairo_t *cr, - Display *dpy, - Drawable drawable); +cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height); -/* XXX: This is a mess from the user's POV. Should the Visual or the - cairo_format_t control what render format is used? Maybe I can have - cairo_surface_create_for_window with a visual, and - cairo_surface_create_for_pixmap with a cairo_format_t. Would that work? -*/ cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap); - -/* XXX: This has been proposed -cairo_status_t -cairo_xlib_surface_set_size (cairo_surface_t *surface, int width, int height); -*/ +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + int width, + int height); + +void +cairo_xlib_surface_set_size (cairo_surface_t *surface, + int width, + int height); CAIRO_END_DECLS +#else /* CAIRO_HAS_XLIB_SURFACE */ +# error Cairo was not compiled with support for the xlib backend #endif /* CAIRO_HAS_XLIB_SURFACE */ + #endif /* CAIRO_XLIB_H */ diff --git a/src/cairo.c b/src/cairo.c index fd10b5cac..f825c733e 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -34,13 +35,18 @@ * Carl D. Worth <cworth@cworth.org> */ - #include "cairoint.h" +#include "cairo-private.h" + +#include "cairo-arc-private.h" +#include "cairo-path-data-private.h" #define CAIRO_TOLERANCE_MINIMUM 0.0002 /* We're limited by 16 bits of sub-pixel precision */ -#ifdef CAIRO_DO_SANITY_CHECKING #include <assert.h> +#ifdef NDEBUG +#define CAIRO_CHECK_SANITY(cr) +#else static int cairo_sane_state (cairo_t *cr) { @@ -56,6 +62,8 @@ cairo_sane_state (cairo_t *cr) case CAIRO_STATUS_INVALID_MATRIX: case CAIRO_STATUS_NO_TARGET_SURFACE: case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: break; default: return 0; @@ -63,25 +71,38 @@ cairo_sane_state (cairo_t *cr) return 1; } #define CAIRO_CHECK_SANITY(cr) assert(cairo_sane_state ((cr))) -#else -#define CAIRO_CHECK_SANITY(cr) #endif - -/** +/* * cairo_create: + * @target: target surface for the context * - * Creates a new #cairo_t with default values. The target - * surface must be set on the #cairo_t with cairo_set_target_surface(), - * or a backend-specific function like cairo_set_target_image() before - * drawing with the #cairo_t. + * Creates a new #cairo_t with all graphics state parameters set to + * default values and with @target as a target surface. The target + * surface should be constructed with a backend-specific function such + * as cairo_image_surface_create (or any other + * cairo_<backend>_surface_create variant). + * + * This function references @target, so you can immediately + * call cairo_surface_destroy() on it if you don't need to + * maintain a separate reference to it. + * + * Note that there are restrictions on using the same surface in + * multiple contexts at the same time. If, after creating @cr_a with + * @surface you also create @cr_b with the same surface, you must + * ensure that @cr_b has finished using @surface before resuming use + * of @cr_a. Currently, the only way time at which this is guaranteed + * is when the the last reference to @cr_b is released with + * cairo_destroy(). (XXX: We need to add a cairo_finish() call to + * provide a way to achieve this explicitly). See also the + * %CAIRO_STATUS_BAD_NESTING status. * * Return value: a newly allocated #cairo_t with a reference * count of 1. The initial reference count should be released * with cairo_destroy() when you are done using the #cairo_t. - **/ + */ cairo_t * -cairo_create (void) +cairo_create (cairo_surface_t *target) { cairo_t *cr; @@ -92,7 +113,15 @@ cairo_create (void) cr->status = CAIRO_STATUS_SUCCESS; cr->ref_count = 1; - cr->gstate = _cairo_gstate_create (); + _cairo_path_fixed_init (&cr->path); + + if (target == NULL) { + cr->gstate = NULL; + cr->status = CAIRO_STATUS_NULL_POINTER; + return cr; + } + + cr->gstate = _cairo_gstate_create (target); if (cr->gstate == NULL) cr->status = CAIRO_STATUS_NO_MEMORY; @@ -125,7 +154,7 @@ cairo_reference (cairo_t *cr) * * Decreases the reference count on @cr by one. If the result * is zero, then @cr and all associated resources are freed. - * See cairo_destroy(). + * See cairo_reference(). **/ void cairo_destroy (cairo_t *cr) @@ -142,6 +171,8 @@ cairo_destroy (cairo_t *cr) _cairo_gstate_destroy (tmp); } + _cairo_path_fixed_fini (&cr->path); + free (cr); } @@ -170,11 +201,7 @@ cairo_save (cairo_t *cr) if (cr->status) return; - if (cr->gstate) { - top = _cairo_gstate_clone (cr->gstate); - } else { - top = _cairo_gstate_create (); - } + top = _cairo_gstate_clone (cr->gstate); if (top == NULL) { cr->status = CAIRO_STATUS_NO_MEMORY; @@ -216,44 +243,10 @@ cairo_restore (cairo_t *cr) if (cr->status) return; - cr->status = _cairo_gstate_restore_external_state (cr->gstate); - CAIRO_CHECK_SANITY (cr); } slim_hidden_def(cairo_restore); -/** - * cairo_copy: - * @dest: a #cairo_t - * @src: another #cairo_t - * - * This function copies all current state information from src to - * dest. This includes the current point and path, the target surface, - * the transformation matrix, and so forth. - * - * The stack of states saved with cairo_save() is <emphasis>not</emphasis> - * not copied; nor are any saved states on @dest cleared. The - * operation only copies the current state of @src to the current - * state of @dest. - **/ -void -cairo_copy (cairo_t *dest, cairo_t *src) -{ - CAIRO_CHECK_SANITY (src); - CAIRO_CHECK_SANITY (dest); - if (dest->status) - return; - - if (src->status) { - dest->status = src->status; - return; - } - - dest->status = _cairo_gstate_copy (dest->gstate, src->gstate); - CAIRO_CHECK_SANITY (src); - CAIRO_CHECK_SANITY (dest); -} - /* XXX: I want to rethink this API void cairo_push_group (cairo_t *cr) @@ -283,130 +276,176 @@ cairo_pop_group (cairo_t *cr) */ /** - * cairo_set_target_surface: + * cairo_set_operator: * @cr: a #cairo_t - * @surface: a #cairo_surface_t + * @op: a compositing operator, specified as a #cairo_operator_t * - * Directs output for a #cairo_t to a given surface. The surface - * will be referenced by the #cairo_t, so you can immediately - * call cairo_surface_destroy() on it if you don't need to - * keep a reference to it around. + * Sets the compositing operator to be used for all drawing + * operations. See #cairo_operator_t for details on the semantics of + * each available drawing operator. + * + * XXX: I'd also like to direct the reader's attention to some + * (not-yet-written) section on cairo's imaging model. How would I do + * that if such a section existed? (cworth). **/ void -cairo_set_target_surface (cairo_t *cr, cairo_surface_t *surface) +cairo_set_operator (cairo_t *cr, cairo_operator_t op) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_set_target_surface (cr->gstate, surface); + cr->status = _cairo_gstate_set_operator (cr->gstate, op); CAIRO_CHECK_SANITY (cr); } -slim_hidden_def(cairo_set_target_surface); /** - * cairo_set_target_image: - * @cr: a #cairo_t - * @data: a pointer to a buffer supplied by the application - * in which to write contents. - * @format: the format of pixels in the buffer - * @width: the width of the image to be stored in the buffer - * @height: the eight of the image to be stored in the buffer - * @stride: the number of bytes between the start of rows - * in the buffer. Having this be specified separate from @width - * allows for padding at the end of rows, or for writing - * to a subportion of a larger image. - * - * Directs output for a #cairo_t to an in-memory image. The output - * buffer must be kept around until the #cairo_t is destroyed or set - * to to have a different target. The initial contents of @buffer - * will be used as the inital image contents; you must explicitely - * clear the buffer, using, for example, cairo_rectangle() and - * cairo_fill() if you want it cleared. + * cairo_set_source_rgb + * @cr: a cairo context + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Sets the source pattern within @cr to an opaque color. This opaque + * color will then be used for any subsequent drawing operation until + * a new source pattern is set. + * + * The color components are floating point numbers in the range 0 to + * 1. If the values passed in are outside that range, they will be + * clamped. **/ void -cairo_set_target_image (cairo_t *cr, - char *data, - cairo_format_t format, - int width, - int height, - int stride) +cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) { - cairo_surface_t *surface; + cairo_color_t color; CAIRO_CHECK_SANITY (cr); if (cr->status) return; - surface = cairo_surface_create_for_image (data, - format, - width, height, stride); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - CAIRO_CHECK_SANITY (cr); - return; - } + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); - cairo_set_target_surface (cr, surface); + _cairo_color_init_rgb (&color, red, green, blue); - cairo_surface_destroy (surface); + cr->status = _cairo_gstate_set_source_solid (cr->gstate, &color); + CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_source_rgba: + * @cr: a cairo context + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Sets the source pattern within @cr to a translucent color. This + * color will then be used for any subsequent drawing operation until + * a new source pattern is set. + * + * The color and alpha components are floating point numbers in the + * range 0 to 1. If the values passed in are outside that range, they + * will be clamped. + **/ void -cairo_set_operator (cairo_t *cr, cairo_operator_t op) +cairo_set_source_rgba (cairo_t *cr, + double red, double green, double blue, + double alpha) { + cairo_color_t color; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_set_operator (cr->gstate, op); + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + _cairo_restrict_value (&alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + + cr->status = _cairo_gstate_set_source_solid (cr->gstate, &color); + CAIRO_CHECK_SANITY (cr); } -/** - * cairo_set_rgb_color: - * @cr: a #cairo_t - * @red: red component of color - * @green: green component of color - * @blue: blue component of color - * - * Sets a constant color for filling and stroking. This replaces any - * pattern set with cairo_set_pattern(). The color components are - * floating point numbers in the range 0 to 1. If the values passed in - * are outside that range, they will be clamped. - **/ void -cairo_set_rgb_color (cairo_t *cr, double red, double green, double blue) +cairo_set_source_surface (cairo_t *cr, + cairo_surface_t *surface, + double x, + double y) { + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); + pattern = cairo_pattern_create_for_surface (surface); + if (!pattern) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cairo_matrix_init_translate (&matrix, -x, -y); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); - cr->status = _cairo_gstate_set_rgb_color (cr->gstate, red, green, blue); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_source + * @cr: a cairo context + * @source: a #cairo_pattern_t to be used as the source for + * subsequent drawing operations. + * + * Sets the source pattern within @cr to @source. This pattern + * will then be used for any subsequent drawing operation until a new + * source pattern is set. + * + * XXX: I'd also like to direct the reader's attention to some + * (not-yet-written) section on cairo's imaging model. How would I do + * that if such a section existed? (cworth). + **/ void -cairo_set_pattern (cairo_t *cr, cairo_pattern_t *pattern) +cairo_set_source (cairo_t *cr, cairo_pattern_t *source) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_set_pattern (cr->gstate, pattern); + cr->status = _cairo_gstate_set_source (cr->gstate, source); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_get_source: + * @cr: a cairo context + * + * Gets the current source pattern for @cr. + * + * Return value: the current source pattern. This object is owned by + * cairo. To keep a reference to it, you must call + * cairo_pattern_reference(). + **/ cairo_pattern_t * -cairo_current_pattern (cairo_t *cr) +cairo_get_source (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_pattern (cr->gstate); + /* XXX: We'll want to do something like this: + if (cr->status) + return cairo_pattern_nil; + */ + + return _cairo_gstate_get_source (cr->gstate); } /** @@ -436,30 +475,17 @@ cairo_set_tolerance (cairo_t *cr, double tolerance) } /** - * cairo_set_alpha: + * cairo_set_fill_rule: * @cr: a #cairo_t - * @alpha: the alpha value. 0 is transparent, 1 fully opaque. - * if the value is outside the range 0 to 1, it will be - * clamped to that range. - * - * Sets an overall alpha value used for stroking and filling. This - * value is multiplied with any alpha value coming from a gradient or - * image pattern. + * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t + * + * Set the current fill rule within the cairo context. The fill rule + * is used to determine which regions are inside or outside a complex + * (potentially self-intersecting) path. The current fill rule affects + * both cairo_fill and cairo_clip. See #cairo_fill_rule_t for details + * on the semantics of each available fill rule. **/ void -cairo_set_alpha (cairo_t *cr, double alpha) -{ - CAIRO_CHECK_SANITY (cr); - if (cr->status) - return; - - _cairo_restrict_value (&alpha, 0.0, 1.0); - - cr->status = _cairo_gstate_set_alpha (cr->gstate, alpha); - CAIRO_CHECK_SANITY (cr); -} - -void cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) { CAIRO_CHECK_SANITY (cr); @@ -470,6 +496,20 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_line_width: + * @cr: a #cairo_t + * @width: a line width, as a user-space value + * + * Sets the current line width within the cairo context. The line + * width specifies the diameter of a pen that is circular in + * user-space. + * + * As with the other stroke parameters, the current line cap style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + **/ void cairo_set_line_width (cairo_t *cr, double width) { @@ -483,6 +523,20 @@ cairo_set_line_width (cairo_t *cr, double width) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_line_cap: + * @cr: a cairo context, as a #cairo_t + * @line_cap: a line cap style, as a #cairo_line_cap_t + * + * Sets the current line cap style within the cairo context. See + * #cairo_line_cap_t for details about how the available line cap + * styles are drawn. + * + * As with the other stroke parameters, the current line cap style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + **/ void cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) { @@ -494,6 +548,20 @@ cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_line_join: + * @cr: a cairo context, as a #cairo_t + * @line_join: a line joint style, as a #cairo_line_join_t + * + * Sets the current line join style within the cairo context. See + * #cairo_line_join_t for details about how the available line join + * styles are drawn. + * + * As with the other stroke parameters, the current line join style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + **/ void cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) { @@ -527,6 +595,19 @@ cairo_set_miter_limit (cairo_t *cr, double limit) CAIRO_CHECK_SANITY (cr); } + +/** + * cairo_translate: + * @cr: a cairo context + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Modifies the current transformation matrix (CTM) by tanslating the + * user-space origin by (@tx, @ty). This offset is interpreted as a + * user-space coordinate according to the CTM in place before the new + * call to cairo_translate. In other words, the translation of the + * user-space origin takes place after any existing transformation. + **/ void cairo_translate (cairo_t *cr, double tx, double ty) { @@ -538,6 +619,17 @@ cairo_translate (cairo_t *cr, double tx, double ty) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_scale: + * @cr: a cairo context + * @sx: scale factor for the X dimension + * @sy: scale factor for the Y dimension + * + * Modifies the current transformation matrix (CTM) by scaling the X + * and Y user-space axes by @sx and @sy respectively. The scaling of + * the axes takes place after any existing transformation of user + * space. + **/ void cairo_scale (cairo_t *cr, double sx, double sy) { @@ -549,6 +641,19 @@ cairo_scale (cairo_t *cr, double sx, double sy) CAIRO_CHECK_SANITY (cr); } + +/** + * cairo_rotate: + * @cr: a cairo context + * @angle: angle (in radians) by which the user-space axes will be + * rotated + * + * Modifies the current transformation matrix (CTM) by rotating the + * user-space axes by @angle radians. The rotation of the axes takes + * places after any existing transformation of user space. The + * rotation direction for positive angles is from the positive X axis + * toward the positive Y axis. + **/ void cairo_rotate (cairo_t *cr, double angle) { @@ -560,21 +665,38 @@ cairo_rotate (cairo_t *cr, double angle) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_transform: + * @cr: a cairo context + * @matrix: a transformation to be applied to the user-space axes + * + * Modifies the current transformation matrix (CTM) by applying + * @matrix as an additional transformation. The new transformation of + * user space takes place after any existing transformation. + **/ void -cairo_concat_matrix (cairo_t *cr, - cairo_matrix_t *matrix) +cairo_transform (cairo_t *cr, + const cairo_matrix_t *matrix) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_concat_matrix (cr->gstate, matrix); + cr->status = _cairo_gstate_transform (cr->gstate, matrix); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_matrix: + * @cr: a cairo context + * @matrix: a transformation matrix from user space to device space + * + * Modifies the current transformation matrix (CTM) by setting it + * equal to @matrix. + **/ void -cairo_set_matrix (cairo_t *cr, - cairo_matrix_t *matrix) +cairo_set_matrix (cairo_t *cr, + const cairo_matrix_t *matrix) { CAIRO_CHECK_SANITY (cr); if (cr->status) @@ -584,17 +706,15 @@ cairo_set_matrix (cairo_t *cr, CAIRO_CHECK_SANITY (cr); } -void -cairo_default_matrix (cairo_t *cr) -{ - CAIRO_CHECK_SANITY (cr); - if (cr->status) - return; - - cr->status = _cairo_gstate_default_matrix (cr->gstate); - CAIRO_CHECK_SANITY (cr); -} - +/** + * cairo_identity_matrix: + * @cr: a cairo context + * + * Resets the current transformation matrix (CTM) by setting it equal + * to the identity matrix. That is, the user-space and device-space + * axes will be aligned and one user-space unit will transform to one + * device-space unit. + **/ void cairo_identity_matrix (cairo_t *cr) { @@ -606,47 +726,89 @@ cairo_identity_matrix (cairo_t *cr) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_user_to_device: + * @cr: a cairo context + * @x: X value of coordinate (in/out parameter) + * @y: Y value of coordinate (in/out parameter) + * + * Transform a coordinate from user space to device space by + * multiplying the given point by the current transformation matrix + * (CTM). + **/ void -cairo_transform_point (cairo_t *cr, double *x, double *y) +cairo_user_to_device (cairo_t *cr, double *x, double *y) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_transform_point (cr->gstate, x, y); + cr->status = _cairo_gstate_user_to_device (cr->gstate, x, y); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_user_to_device_distance: + * @cr: a cairo context + * @dx: X component of a distance vector (in/out parameter) + * @dy: Y component of a distance vector (in/out parameter) + * + * Transform a distance vector from user space to device space. This + * function is similar to cairo_user_to_device() except that the + * translation components of the CTM will be ignored when transforming + * (@dx,@dy). + **/ void -cairo_transform_distance (cairo_t *cr, double *dx, double *dy) +cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_transform_distance (cr->gstate, dx, dy); + cr->status = _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_device_to_user: + * @cr: a cairo + * @x: X value of coordinate (in/out parameter) + * @y: Y value of coordinate (in/out parameter) + * + * Transform a coordinate from device space to user space by + * multiplying the given point by the inverse of the current + * transformation matrix (CTM). + **/ void -cairo_inverse_transform_point (cairo_t *cr, double *x, double *y) +cairo_device_to_user (cairo_t *cr, double *x, double *y) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_inverse_transform_point (cr->gstate, x, y); + cr->status = _cairo_gstate_device_to_user (cr->gstate, x, y); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_device_to_user_distance: + * @cr: a cairo context + * @dx: X component of a distance vector (in/out parameter) + * @dy: Y component of a distance vector (in/out parameter) + * + * Transform a distance vector from device space to user space. This + * function is similar to cairo_device_to_user() except that the + * translation components of the inverse CTM will be ignored when + * transforming (@dx,@dy). + **/ void -cairo_inverse_transform_distance (cairo_t *cr, double *dx, double *dy) +cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_inverse_transform_distance (cr->gstate, dx, dy); + cr->status = _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy); CAIRO_CHECK_SANITY (cr); } @@ -657,18 +819,27 @@ cairo_new_path (cairo_t *cr) if (cr->status) return; - cr->status = _cairo_gstate_new_path (cr->gstate); + _cairo_path_fixed_fini (&cr->path); + CAIRO_CHECK_SANITY (cr); } +slim_hidden_def(cairo_new_path); void cairo_move_to (cairo_t *cr, double x, double y) { + cairo_fixed_t x_fixed, y_fixed; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_move_to (cr->gstate, x, y); + _cairo_gstate_user_to_backend (cr->gstate, &x, &y); + x_fixed = _cairo_fixed_from_double (x); + y_fixed = _cairo_fixed_from_double (y); + + cr->status = _cairo_path_fixed_move_to (&cr->path, x_fixed, y_fixed); + CAIRO_CHECK_SANITY (cr); } slim_hidden_def(cairo_move_to); @@ -676,11 +847,18 @@ slim_hidden_def(cairo_move_to); void cairo_line_to (cairo_t *cr, double x, double y) { + cairo_fixed_t x_fixed, y_fixed; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_line_to (cr->gstate, x, y); + _cairo_gstate_user_to_backend (cr->gstate, &x, &y); + x_fixed = _cairo_fixed_from_double (x); + y_fixed = _cairo_fixed_from_double (y); + + cr->status = _cairo_path_fixed_line_to (&cr->path, x_fixed, y_fixed); + CAIRO_CHECK_SANITY (cr); } @@ -690,20 +868,38 @@ cairo_curve_to (cairo_t *cr, double x2, double y2, double x3, double y3) { + cairo_fixed_t x1_fixed, y1_fixed; + cairo_fixed_t x2_fixed, y2_fixed; + cairo_fixed_t x3_fixed, y3_fixed; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_curve_to (cr->gstate, - x1, y1, - x2, y2, - x3, y3); + _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1); + _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2); + _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3); + + x1_fixed = _cairo_fixed_from_double (x1); + y1_fixed = _cairo_fixed_from_double (y1); + + x2_fixed = _cairo_fixed_from_double (x2); + y2_fixed = _cairo_fixed_from_double (y2); + + x3_fixed = _cairo_fixed_from_double (x3); + y3_fixed = _cairo_fixed_from_double (y3); + + cr->status = _cairo_path_fixed_curve_to (&cr->path, + x1_fixed, y1_fixed, + x2_fixed, y2_fixed, + x3_fixed, y3_fixed); + CAIRO_CHECK_SANITY (cr); } /** * cairo_arc: - * @cr: a Cairo context + * @cr: a cairo context * @xc: X position of the center of the arc * @yc: Y position of the center of the arc * @radius: the radius of the arc @@ -744,16 +940,26 @@ cairo_arc (cairo_t *cr, if (cr->status) return; - cr->status = _cairo_gstate_arc (cr->gstate, - xc, yc, - radius, - angle1, angle2); + /* Do nothing, successfully, if radius is <= 0 */ + if (radius <= 0.0) + return; + + while (angle2 < angle1) + angle2 += 2 * M_PI; + + cairo_line_to (cr, + xc + radius * cos (angle1), + yc + radius * sin (angle1)); + + _cairo_arc_path (cr, xc, yc, radius, + angle1, angle2); + CAIRO_CHECK_SANITY (cr); } /** * cairo_arc_negative: - * @cr: a Cairo context + * @cr: a cairo context * @xc: X position of the center of the arc * @yc: Y position of the center of the arc * @radius: the radius of the arc @@ -775,10 +981,20 @@ cairo_arc_negative (cairo_t *cr, if (cr->status) return; - cr->status = _cairo_gstate_arc_negative (cr->gstate, - xc, yc, - radius, - angle1, angle2); + /* Do nothing, successfully, if radius is <= 0 */ + if (radius <= 0.0) + return; + + while (angle2 > angle1) + angle2 -= 2 * M_PI; + + cairo_line_to (cr, + xc + radius * cos (angle1), + yc + radius * sin (angle1)); + + _cairo_arc_path_negative (cr, xc, yc, radius, + angle1, angle2); + CAIRO_CHECK_SANITY (cr); } @@ -802,22 +1018,36 @@ cairo_arc_to (cairo_t *cr, void cairo_rel_move_to (cairo_t *cr, double dx, double dy) { + cairo_fixed_t dx_fixed, dy_fixed; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_rel_move_to (cr->gstate, dx, dy); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); + dx_fixed = _cairo_fixed_from_double (dx); + dy_fixed = _cairo_fixed_from_double (dy); + + cr->status = _cairo_path_fixed_rel_move_to (&cr->path, dx_fixed, dy_fixed); + CAIRO_CHECK_SANITY (cr); } void cairo_rel_line_to (cairo_t *cr, double dx, double dy) { + cairo_fixed_t dx_fixed, dy_fixed; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_rel_line_to (cr->gstate, dx, dy); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); + dx_fixed = _cairo_fixed_from_double (dx); + dy_fixed = _cairo_fixed_from_double (dy); + + cr->status = _cairo_path_fixed_rel_line_to (&cr->path, dx_fixed, dy_fixed); + CAIRO_CHECK_SANITY (cr); } slim_hidden_def(cairo_rel_line_to); @@ -828,14 +1058,32 @@ cairo_rel_curve_to (cairo_t *cr, double dx2, double dy2, double dx3, double dy3) { + cairo_fixed_t dx1_fixed, dy1_fixed; + cairo_fixed_t dx2_fixed, dy2_fixed; + cairo_fixed_t dx3_fixed, dy3_fixed; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_rel_curve_to (cr->gstate, - dx1, dy1, - dx2, dy2, - dx3, dy3); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2); + _cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3); + + dx1_fixed = _cairo_fixed_from_double (dx1); + dy1_fixed = _cairo_fixed_from_double (dy1); + + dx2_fixed = _cairo_fixed_from_double (dx2); + dy2_fixed = _cairo_fixed_from_double (dy2); + + dx3_fixed = _cairo_fixed_from_double (dx3); + dy3_fixed = _cairo_fixed_from_double (dy3); + + cr->status = _cairo_path_fixed_rel_curve_to (&cr->path, + dx1_fixed, dy1_fixed, + dx2_fixed, dy2_fixed, + dx3_fixed, dy3_fixed); + CAIRO_CHECK_SANITY (cr); } @@ -858,7 +1106,7 @@ cairo_rectangle (cairo_t *cr, /* XXX: NYI void -cairo_stroke_path (cairo_t *cr) +cairo_stroke_to_path (cairo_t *cr) { if (cr->status) return; @@ -874,32 +1122,208 @@ cairo_close_path (cairo_t *cr) if (cr->status) return; - cr->status = _cairo_gstate_close_path (cr->gstate); + cr->status = _cairo_path_fixed_close_path (&cr->path); + CAIRO_CHECK_SANITY (cr); } slim_hidden_def(cairo_close_path); +/** + * cairo_paint: + * @cr: a cairo context + * + * A drawing operator that paints the current source everywhere within + * the current clip region. + **/ +void +cairo_paint (cairo_t *cr) +{ + CAIRO_CHECK_SANITY (cr); + if (cr->status) + return; + + cr->status = _cairo_gstate_paint (cr->gstate); + + CAIRO_CHECK_SANITY (cr); +} + +/** + * cairo_paint_with_alpha: + * @cr: a cairo context + * @alpha: alpha value, between 0 (transparent) and 1 (opaque) + * + * A drawing operator that paints the current source everywhere within + * the current clip region using a mask of constant alpha value + * @alpha. The effect is similar to cairo_paint(), but the drawing + * is faded out using the alpha value. + **/ +void +cairo_paint_with_alpha (cairo_t *cr, + double alpha) +{ + cairo_color_t color; + cairo_pattern_union_t pattern; + + CAIRO_CHECK_SANITY (cr); + if (cr->status) + return; + + _cairo_color_init_rgba (&color, 1., 1., 1., alpha); + _cairo_pattern_init_solid (&pattern.solid, &color); + + cr->status = _cairo_gstate_mask (cr->gstate, &pattern.base); + + _cairo_pattern_fini (&pattern.base); + + CAIRO_CHECK_SANITY (cr); +} + +/** + * cairo_mask: + * @cr: a cairo context + * @pattern: a #cairo_pattern_t + * + * A drawing operator that paints the current source + * using the alpha channel of @pattern as a mask. (Opaque + * areas of @mask are painted with the source, transparent + * areas are not painted.) + */ +void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern) +{ + CAIRO_CHECK_SANITY (cr); + if (cr->status) + return; + + cr->status = _cairo_gstate_mask (cr->gstate, pattern); + + CAIRO_CHECK_SANITY (cr); +} + +/** + * cairo_mask_surface: + * @cr: a cairo context + * @surface: a #cairo_surface_t + * @surface_x: X coordinate at which to place the origin of @surface + * @surface_y: Y coordinate at which to place the origin of @surface + * + * A drawing operator that paints the current source + * using the alpha channel of @surface as a mask. (Opaque + * areas of @surface are painted with the source, transparent + * areas are not painted.) + */ +void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + CAIRO_CHECK_SANITY (cr); + if (cr->status) + return; + + pattern = cairo_pattern_create_for_surface (surface); + if (!pattern) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); +} + +/** + * cairo_stroke: + * @cr: a cairo context + * + * A drawing operator that strokes the current path according to the + * current line width, line join, line cap, and dash settings. After + * cairo_stroke, the current path will be cleared from the cairo + * context. See cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + **/ void cairo_stroke (cairo_t *cr) { + cairo_stroke_preserve (cr); + + cairo_new_path (cr); +} + +/** + * cairo_stroke_preserve: + * @cr: a cairo context + * + * A drawing operator that strokes the current path according to the + * current line width, line join, line cap, and dash settings. Unlike + * cairo_stroke(), cairo_stroke_preserve preserves the path within the + * cairo context. + * + * See cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + **/ +void +cairo_stroke_preserve (cairo_t *cr) +{ CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_stroke (cr->gstate); + cr->status = _cairo_gstate_stroke (cr->gstate, &cr->path); + CAIRO_CHECK_SANITY (cr); } +slim_hidden_def(cairo_stroke_preserve); +/** + * cairo_fill: + * @cr: a cairo context + * + * A drawing operator that fills the current path according to the + * current fill rule. After cairo_fill, the current path will be + * cleared from the cairo context. See cairo_set_fill_rule() and + * cairo_fill_preserve(). + **/ void cairo_fill (cairo_t *cr) { + cairo_fill_preserve (cr); + + cairo_new_path (cr); +} + +/** + * cairo_fill_preserve: + * @cr: a cairo context + * + * A drawing operator that fills the current path according to the + * current fill rule. Unlike cairo_fill(), cairo_fill_preserve + * preserves the path within the cairo context. + * + * See cairo_set_fill_rule() and cairo_fill(). + **/ +void +cairo_fill_preserve (cairo_t *cr) +{ CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_fill (cr->gstate); + cr->status = _cairo_gstate_fill (cr->gstate, &cr->path); + CAIRO_CHECK_SANITY (cr); } +slim_hidden_def(cairo_fill_preserve); void cairo_copy_page (cairo_t *cr) @@ -932,7 +1356,9 @@ cairo_in_stroke (cairo_t *cr, double x, double y) if (cr->status) return 0; - cr->status = _cairo_gstate_in_stroke (cr->gstate, x, y, &inside); + cr->status = _cairo_gstate_in_stroke (cr->gstate, + &cr->path, + x, y, &inside); CAIRO_CHECK_SANITY (cr); @@ -951,7 +1377,9 @@ cairo_in_fill (cairo_t *cr, double x, double y) if (cr->status) return 0; - cr->status = _cairo_gstate_in_fill (cr->gstate, x, y, &inside); + cr->status = _cairo_gstate_in_fill (cr->gstate, + &cr->path, + x, y, &inside); CAIRO_CHECK_SANITY (cr); @@ -969,7 +1397,9 @@ cairo_stroke_extents (cairo_t *cr, if (cr->status) return; - cr->status = _cairo_gstate_stroke_extents (cr->gstate, x1, y1, x2, y2); + cr->status = _cairo_gstate_stroke_extents (cr->gstate, + &cr->path, + x1, y1, x2, y2); CAIRO_CHECK_SANITY (cr); } @@ -981,143 +1411,288 @@ cairo_fill_extents (cairo_t *cr, if (cr->status) return; - cr->status = _cairo_gstate_fill_extents (cr->gstate, x1, y1, x2, y2); + cr->status = _cairo_gstate_fill_extents (cr->gstate, + &cr->path, + x1, y1, x2, y2); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_clip: + * @cr: a cairo context + * + * Establishes a new clip region by intersecting the current clip + * region with the current path as it would be filled by cairo_fill() + * and according to the current fill rule (see cairo_set_fill_rule()). + * + * After cairo_clip, the current path will be cleared from the cairo + * context. + * + * The current clip region affects all drawing operations by + * effectively masking out any changes to the surface that are outside + * the current clip region. + * + * Calling cairo_clip() can only make the clip region smaller, never + * larger. But the current clip is part of the graphics state, so a + * tempoarary restriction of the clip region can be achieved by + * calling cairo_clip() within a cairo_save()/cairo_restore() + * pair. The only other means of increasing the size of the clip + * region is cairo_reset_clip(). + **/ +void +cairo_clip (cairo_t *cr) +{ + cairo_clip_preserve (cr); + + cairo_new_path (cr); +} + +/** + * cairo_clip_preserve: + * @cr: a cairo context + * + * Establishes a new clip region by intersecting the current clip + * region with the current path as it would be filled by cairo_fill() + * and according to the current fill rule (see cairo_set_fill_rule()). + * + * Unlike cairo_clip(), cairo_clip_preserve preserves the path within + * the cairo context. + * + * The current clip region affects all drawing operations by + * effectively masking out any changes to the surface that are outside + * the current clip region. + * + * Calling cairo_clip() can only make the clip region smaller, never + * larger. But the current clip is part of the graphics state, so a + * tempoarary restriction of the clip region can be achieved by + * calling cairo_clip() within a cairo_save()/cairo_restore() + * pair. The only other means of increasing the size of the clip + * region is cairo_reset_clip(). + **/ void -cairo_init_clip (cairo_t *cr) +cairo_clip_preserve (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_init_clip (cr->gstate); + cr->status = _cairo_gstate_clip (cr->gstate, &cr->path); CAIRO_CHECK_SANITY (cr); } +slim_hidden_def(cairo_clip_preserve); +/** + * cairo_reset_clip: + * @cr: a cairo context + * + * Reset the current clip region to its original, unrestricted + * state. That is, set the clip region to an infinitely large shape + * containing the target surface. Equivalently, if infinity is too + * hard to grasp, one can imagine the clip region being reset to the + * exact bounds of the target surface. + * + * Note that code meant to be reusable should not call + * cairo_reset_clip() as it will cause results unexpected by + * higher-level code which calls cairo_clip(). Consider using + * cairo_save() and cairo_restore() around cairo_clip() as a more + * robust means of temporarily restricting the clip region. + **/ void -cairo_clip (cairo_t *cr) +cairo_reset_clip (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_clip (cr->gstate); + cr->status = _cairo_gstate_reset_clip (cr->gstate); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_select_font_face: + * @cr: a #cairo_t + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Selects a family and style of font from a simplified description as + * a family name, slant and weight. This function is meant to be used + * only for applications with simple font needs: Cairo doesn't provide + * for operations such as listing all available fonts on the system, + * and it is expected that most applications will need to use a more + * comprehensive font handling and text layout library in addition to + * Cairo. + **/ void -cairo_select_font (cairo_t *cr, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) +cairo_select_font_face (cairo_t *cr, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_select_font (cr->gstate, family, slant, weight); + cr->status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight); CAIRO_CHECK_SANITY (cr); } /** - * cairo_current_font: + * cairo_get_font_face: * @cr: a #cairo_t * - * Gets the current font object for a #cairo_t. If there is no current - * font object, because the font parameters, transform, or target - * surface has been changed since a font was last used, a font object - * will be created and stored in in the #cairo_t. + * Gets the current font face for a #cairo_t. * * Return value: the current font object. Can return %NULL * on out-of-memory or if the context is already in - * an error state. This object is owned by Cairo. To keep + * an error state. This object is owned by cairo. To keep * a reference to it, you must call cairo_font_reference(). **/ -cairo_font_t * -cairo_current_font (cairo_t *cr) +cairo_font_face_t * +cairo_get_font_face (cairo_t *cr) { - cairo_font_t *ret; + cairo_font_face_t *font_face; CAIRO_CHECK_SANITY (cr); if (cr->status) return NULL; - cr->status = _cairo_gstate_current_font (cr->gstate, &ret); + cr->status = _cairo_gstate_get_font_face (cr->gstate, &font_face); CAIRO_CHECK_SANITY (cr); - return ret; + return font_face; } +/** + * cairo_font_extents: + * @cr: a #cairo_t + * @extents: a #cairo_font_extents_t object into which the results + * will be stored. + * + * Gets the font extents for the currently selected font. + **/ void -cairo_current_font_extents (cairo_t *cr, - cairo_font_extents_t *extents) +cairo_font_extents (cairo_t *cr, + cairo_font_extents_t *extents) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_current_font_extents (cr->gstate, extents); + cr->status = _cairo_gstate_get_font_extents (cr->gstate, extents); CAIRO_CHECK_SANITY (cr); } - /** - * cairo_set_font: + * cairo_set_font_face: * @cr: a #cairo_t - * @font: a #cairo_font_t, or %NULL to unset any previously set font. - * - * Replaces the current #cairo_font_t object in the #cairo_t with - * @font. The replaced font in the #cairo_t will be destroyed if there - * are no other references to it. Since a #cairo_font_t is specific to - * a particular output device and size, changing the transformation, - * font transformation, or target surfaces of a #cairo_t will clear - * any previously set font. Setting the font using cairo_set_font() is - * exclusive with the simple font selection API provided by - * cairo_select_font(). The size and transformation set by - * cairo_scale_font() and cairo_transform_font() are ignored unless - * they were taken into account when creating @font. + * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font + * + * Replaces the current #cairo_font_face_t object in the #cairo_t with + * @font_face. The replaced font face in the #cairo_t will be + * destroyed if there are no other references to it. **/ void -cairo_set_font (cairo_t *cr, cairo_font_t *font) +cairo_set_font_face (cairo_t *cr, + cairo_font_face_t *font_face) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_set_font (cr->gstate, font); + cr->status = _cairo_gstate_set_font_face (cr->gstate, font_face); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_font_size: + * @cr: a #cairo_t + * @size: the new font size, in user space units + * + * Sets the current font matrix to a scale by a factor of @size, replacing + * any font matrix previously set with cairo_set_font_size() or + * cairo_set_font_matrix(). This results in a font size of @size user space + * units. (More precisely, this matrix will result in the font's + * em-square being a @size by @size square in user space.) + **/ void -cairo_scale_font (cairo_t *cr, double scale) +cairo_set_font_size (cairo_t *cr, double size) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_scale_font (cr->gstate, scale); + cr->status = _cairo_gstate_set_font_size (cr->gstate, size); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_font_matrix + * @cr: a #cairo_t + * @matrix: a #cairo_matrix_t describing a transform to be applied to + * the current font. + * + * Sets the current font matrix to @matrix. The font matrix gives a + * transformation from the design space of the font (in this space, + * the em-square is 1 unit by 1 unit) to user space. Normally, a + * simple scale is used (see cairo_set_font_size()), but a more + * complex font matrix can be used to shear the font + * or stretch it unequally along the two axes + **/ void -cairo_transform_font (cairo_t *cr, cairo_matrix_t *matrix) +cairo_set_font_matrix (cairo_t *cr, + const cairo_matrix_t *matrix) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_transform_font (cr->gstate, matrix); + cr->status = _cairo_gstate_set_font_matrix (cr->gstate, matrix); CAIRO_CHECK_SANITY (cr); } +/** + * cairo_get_font_matrix + * @cr: a #cairo_t + * @matrix: return value for the matrix + * + * Stores the current font matrix into @matrix. See + * cairo_set_font_matrix(). + **/ +void +cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) +{ + CAIRO_CHECK_SANITY (cr); + _cairo_gstate_get_font_matrix (cr->gstate, matrix); +} + +/** + * cairo_text_extents: + * @cr: a #cairo_t + * @utf8: a string of text, encoded in utf-8 + * @extents: a #cairo_text_extents_t object into which the results + * will be stored. + * + * Gets the extents for a string of text. The extents describe a + * user-space rectangle that encloses the "inked" portion of the text, + * (as it would be drawn by cairo_show_text). Additionally, the + * x_advance and y_advance values indicate the amount by which the + * current point would be advanced by cairo_show_text. + * + * Note that whitespace characters do not directly contribute to the + * size of the rectangle (extents.width and extents.height). They do + * contribute indirectly by changing the position of non-whitespace + * characters. In particular, trailing whitespace characters are + * likely to not affect the size of the rectangle, though they will + * affect the x_advance and y_advance values. + **/ void -cairo_text_extents (cairo_t *cr, - const unsigned char *utf8, - cairo_text_extents_t *extents) +cairo_text_extents (cairo_t *cr, + const char *utf8, + cairo_text_extents_t *extents) { cairo_glyph_t *glyphs = NULL; - int nglyphs; + int num_glyphs; + double x, y; CAIRO_CHECK_SANITY (cr); if (cr->status) @@ -1133,7 +1708,11 @@ cairo_text_extents (cairo_t *cr, return; } - cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); + cairo_get_current_point (cr, &x, &y); + + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, + x, y, + &glyphs, &num_glyphs); CAIRO_CHECK_SANITY (cr); if (cr->status) { @@ -1142,13 +1721,31 @@ cairo_text_extents (cairo_t *cr, return; } - cr->status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, nglyphs, extents); + cr->status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents); CAIRO_CHECK_SANITY (cr); if (glyphs) free (glyphs); } +/** + * cairo_glyph_extents: + * @cr: a #cairo_t + * @glyphs: an array of #cairo_glyph_t objects + * @num_glyphs: the number of elements in @glyphs + * @extents: a #cairo_text_extents_t object into which the results + * will be stored + * + * Gets the extents for an array of glyphs. The extents describe a + * user-space rectangle that encloses the "inked" portion of the + * glyphs, (as they would be drawn by cairo_show_glyphs). + * Additionally, the x_advance and y_advance values indicate the + * amount by which the current point would be advanced by + * cairo_show_glyphs. + * + * Note that whitespace glyphs do not contribute to the size of the + * rectangle (extents.width and extents.height). + **/ void cairo_glyph_extents (cairo_t *cr, cairo_glyph_t *glyphs, @@ -1165,10 +1762,11 @@ cairo_glyph_extents (cairo_t *cr, } void -cairo_show_text (cairo_t *cr, const unsigned char *utf8) +cairo_show_text (cairo_t *cr, const char *utf8) { cairo_glyph_t *glyphs = NULL; - int nglyphs; + int num_glyphs; + double x, y; CAIRO_CHECK_SANITY (cr); if (cr->status) @@ -1177,7 +1775,11 @@ cairo_show_text (cairo_t *cr, const unsigned char *utf8) if (utf8 == NULL) return; - cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); + cairo_get_current_point (cr, &x, &y); + + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, + x, y, + &glyphs, &num_glyphs); CAIRO_CHECK_SANITY (cr); if (cr->status) { @@ -1186,7 +1788,7 @@ cairo_show_text (cairo_t *cr, const unsigned char *utf8) return; } - cr->status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, nglyphs); + cr->status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, num_glyphs); CAIRO_CHECK_SANITY (cr); if (glyphs) @@ -1205,16 +1807,21 @@ cairo_show_glyphs (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs) } void -cairo_text_path (cairo_t *cr, const unsigned char *utf8) +cairo_text_path (cairo_t *cr, const char *utf8) { cairo_glyph_t *glyphs = NULL; - int nglyphs; + int num_glyphs; + double x, y; CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); + cairo_get_current_point (cr, &x, &y); + + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, + x, y, + &glyphs, &num_glyphs); CAIRO_CHECK_SANITY (cr); if (cr->status) { @@ -1223,7 +1830,9 @@ cairo_text_path (cairo_t *cr, const unsigned char *utf8) return; } - cr->status = _cairo_gstate_glyph_path (cr->gstate, glyphs, nglyphs); + cr->status = _cairo_gstate_glyph_path (cr->gstate, + glyphs, num_glyphs, + &cr->path); CAIRO_CHECK_SANITY (cr); if (glyphs) @@ -1237,162 +1846,281 @@ cairo_glyph_path (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs) if (cr->status) return; - cr->status = _cairo_gstate_glyph_path (cr->gstate, glyphs, num_glyphs); + cr->status = _cairo_gstate_glyph_path (cr->gstate, + glyphs, num_glyphs, + &cr->path); + CAIRO_CHECK_SANITY (cr); } -void -cairo_show_surface (cairo_t *cr, - cairo_surface_t *surface, - int width, - int height) +/** + * cairo_get_operator: + * @cr: a cairo context + * + * Gets the current compositing operator for a cairo context. + * + * Return value: the current compositing operator. + **/ +cairo_operator_t +cairo_get_operator (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - if (cr->status) - return; - - cr->status = _cairo_gstate_show_surface (cr->gstate, - surface, width, height); - CAIRO_CHECK_SANITY (cr); + return _cairo_gstate_get_operator (cr->gstate); } -cairo_operator_t -cairo_current_operator (cairo_t *cr) +/** + * cairo_get_tolerance: + * @cr: a cairo context + * + * Gets the current tolerance value, as set by cairo_set_tolerance(). + * + * Return value: the current tolerance value. + **/ +double +cairo_get_tolerance (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_operator (cr->gstate); + return _cairo_gstate_get_tolerance (cr->gstate); } -DEPRECATE (cairo_get_operator, cairo_current_operator); +/** + * cairo_get_current_point: + * @cr: a cairo context + * @x: return value for X coordinate of the current point + * @y: return value for Y coordinate of the current point + * + * Gets the current point of the current path, which is + * conceptually the final point reached by the path so far. + * + * The current point is returned in the user-space coordinate + * system. If there is no defined current point then @x and @y will + * both be set to 0.0. + * + * Most path construction functions alter the current point. See the + * following for details on how they affect the current point: + * + * cairo_new_path(), cairo_move_to(), cairo_line_to(), + * cairo_curve_to(), cairo_arc(), cairo_rel_move_to(), + * cairo_rel_line_to(), cairo_rel_curve_to(), cairo_arc(), + * cairo_text_path(), cairo_stroke_to_path() + **/ void -cairo_current_rgb_color (cairo_t *cr, double *red, double *green, double *blue) +cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret) { - CAIRO_CHECK_SANITY (cr); - _cairo_gstate_current_rgb_color (cr->gstate, red, green, blue); - CAIRO_CHECK_SANITY (cr); -} -DEPRECATE (cairo_get_rgb_color, cairo_current_rgb_color); + cairo_status_t status; + cairo_fixed_t x_fixed, y_fixed; + double x, y; -double -cairo_current_alpha (cairo_t *cr) -{ CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_alpha (cr->gstate); -} -DEPRECATE (cairo_get_alpha, cairo_current_alpha); -double -cairo_current_tolerance (cairo_t *cr) -{ - CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_tolerance (cr->gstate); -} -DEPRECATE (cairo_get_tolerance, cairo_current_tolerance); + status = _cairo_path_fixed_get_current_point (&cr->path, &x_fixed, &y_fixed); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { + x = 0.0; + y = 0.0; + } else { + x = _cairo_fixed_to_double (x_fixed); + y = _cairo_fixed_to_double (y_fixed); + _cairo_gstate_backend_to_user (cr->gstate, &x, &y); + } + + if (x_ret) + *x_ret = x; + if (y_ret) + *y_ret = y; -void -cairo_current_point (cairo_t *cr, double *x, double *y) -{ - CAIRO_CHECK_SANITY (cr); - _cairo_gstate_current_point (cr->gstate, x, y); CAIRO_CHECK_SANITY (cr); } -DEPRECATE (cairo_get_current_point, cairo_current_point); +slim_hidden_def(cairo_get_current_point); +/** + * cairo_get_fill_rule: + * @cr: a cairo context + * + * Gets the current fill rule, as set by cairo_set_fill_rule(). + * + * Return value: the current fill rule. + **/ cairo_fill_rule_t -cairo_current_fill_rule (cairo_t *cr) +cairo_get_fill_rule (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_fill_rule (cr->gstate); + return _cairo_gstate_get_fill_rule (cr->gstate); } -DEPRECATE (cairo_get_fill_rule, cairo_current_fill_rule); +/** + * cairo_get_line_width: + * @cr: a cairo context + * + * Gets the current line width, as set by cairo_set_line_width(). + * + * Return value: the current line width, in user-space units. + **/ double -cairo_current_line_width (cairo_t *cr) +cairo_get_line_width (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_line_width (cr->gstate); + return _cairo_gstate_get_line_width (cr->gstate); } -DEPRECATE (cairo_get_line_width, cairo_current_line_width); +/** + * cairo_get_line_cap: + * @cr: a cairo context + * + * Gets the current line cap style, as set by cairo_set_line_cap(). + * + * Return value: the current line cap style. + **/ cairo_line_cap_t -cairo_current_line_cap (cairo_t *cr) +cairo_get_line_cap (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_line_cap (cr->gstate); + return _cairo_gstate_get_line_cap (cr->gstate); } -DEPRECATE (cairo_get_line_cap, cairo_current_line_cap); +/** + * cairo_get_line_join: + * @cr: a cairo context + * + * Gets the current line join style, as set by cairo_set_line_join(). + * + * Return value: the current line join style. + **/ cairo_line_join_t -cairo_current_line_join (cairo_t *cr) +cairo_get_line_join (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_line_join (cr->gstate); + return _cairo_gstate_get_line_join (cr->gstate); } -DEPRECATE (cairo_get_line_join, cairo_current_line_join); +/** + * cairo_get_miter_limit: + * @cr: a cairo context + * + * Gets the current miter limit, as set by cairo_set_miter_limit(). + * + * Return value: the current miter limit. + **/ double -cairo_current_miter_limit (cairo_t *cr) +cairo_get_miter_limit (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_miter_limit (cr->gstate); + return _cairo_gstate_get_miter_limit (cr->gstate); } -DEPRECATE (cairo_get_miter_limit, cairo_current_miter_limit); +/** + * cairo_get_matrix: + * @cr: a cairo context + * @matrix: return value for the matrix + * + * Stores the current transformation matrix (CTM) into @matrix. + **/ void -cairo_current_matrix (cairo_t *cr, cairo_matrix_t *matrix) +cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix) { CAIRO_CHECK_SANITY (cr); - _cairo_gstate_current_matrix (cr->gstate, matrix); - CAIRO_CHECK_SANITY (cr); + _cairo_gstate_get_matrix (cr->gstate, matrix); } -DEPRECATE (cairo_get_matrix, cairo_current_matrix); +/** + * cairo_get_target: + * @cr: a cairo context + * + * Gets the target surface for the cairo context as passed to + * cairo_create(). + * + * Return value: the target surface, (or NULL if @cr is in an error + * state). This object is owned by cairo. To keep a reference to it, + * you must call cairo_pattern_reference(). + **/ cairo_surface_t * -cairo_current_target_surface (cairo_t *cr) +cairo_get_target (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); - return _cairo_gstate_current_target_surface (cr->gstate); + if (cr->status) + return NULL; + + return _cairo_gstate_get_target (cr->gstate); } -DEPRECATE (cairo_get_target_surface, cairo_current_target_surface); -void -cairo_current_path (cairo_t *cr, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_curve_to_func_t *curve_to, - cairo_close_path_func_t *close_path, - void *closure) +/** + * cairo_copy_path: + * @cr: a cairo context + * + * Creates a copy of the current path and returns it to the user as a + * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate + * over the returned data structure. + * + * Return value: the copy of the current path. The caller owns the + * returned object and should call cairo_path_destroy() when finished + * with it. + **/ +cairo_path_t * +cairo_copy_path (cairo_t *cr) { CAIRO_CHECK_SANITY (cr); if (cr->status) - return; - - cr->status = _cairo_gstate_interpret_path (cr->gstate, - move_to, - line_to, - curve_to, - close_path, - closure); + return &_cairo_path_nil; + + return _cairo_path_data_create (&cr->path, cr->gstate); +} + +/** + * cairo_copy_path_flat: + * @cr: a cairo context + * + * Gets a flattened copy of the current path and returns it to the + * user as a #cairo_path_t. See #cairo_path_data_t for hints on + * how to iterate over the returned data structure. + * + * This function is like cairo_copy_path() except that any curves + * in the path will be approximated with piecewise-linear + * approximations, (accurate to within the current tolerance + * value). That is, the result is guaranteed to not have any elements + * of type CAIRO_PATH_CURVE_TO which will instead be replaced by a + * series of CAIRO_PATH_LINE_TO elements. + * + * Return value: the copy of the current path. The caller owns the + * returned object and should call cairo_path_destroy() when finished + * with it. + **/ +cairo_path_t * +cairo_copy_path_flat (cairo_t *cr) +{ CAIRO_CHECK_SANITY (cr); + if (cr->status) + return &_cairo_path_nil; + + return _cairo_path_data_create_flat (&cr->path, cr->gstate); } +/** + * cairo_append_path: + * @cr: a cairo context + * @path: path to be appended + * + * Append the @path onto the current path. See #cairo_path_t + * for details on how the path data structure must be initialized. + **/ void -cairo_current_path_flat (cairo_t *cr, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_close_path_func_t *close_path, - void *closure) +cairo_append_path (cairo_t *cr, + cairo_path_t *path) { CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_interpret_path (cr->gstate, - move_to, - line_to, - NULL, - close_path, - closure); + if (path == NULL || path->data == NULL) { + cr->status = CAIRO_STATUS_NULL_POINTER; + return; + } + + if (path == &_cairo_path_nil) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cr->status = _cairo_path_data_append_to_context (path, cr); + CAIRO_CHECK_SANITY (cr); } @@ -1402,7 +2130,6 @@ cairo_status (cairo_t *cr) CAIRO_CHECK_SANITY (cr); return cr->status; } -DEPRECATE (cairo_get_status, cairo_status); const char * cairo_status_string (cairo_t *cr) @@ -1426,11 +2153,22 @@ cairo_status_string (cairo_t *cr) return "NULL pointer"; case CAIRO_STATUS_INVALID_STRING: return "input string not valid UTF-8"; + case CAIRO_STATUS_INVALID_PATH_DATA: + return "input path data not valid"; + case CAIRO_STATUS_READ_ERROR: + return "error while reading from input stream"; + case CAIRO_STATUS_WRITE_ERROR: + return "error while writing to output stream"; + case CAIRO_STATUS_SURFACE_FINISHED: + return "the target surface has been finished"; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + return "the surface type is not appropriate for the operation"; + case CAIRO_STATUS_BAD_NESTING: + return "drawing operations interleaved for two contexts for the same surface"; } return "<unknown error status>"; } -DEPRECATE (cairo_get_status_string, cairo_status_string); void _cairo_restrict_value (double *value, double min, double max) diff --git a/src/cairo.h b/src/cairo.h index 03e063242..4cb3a023e 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -46,7 +47,6 @@ #endif #include <cairo-features.h> -#include <pixman.h> CAIRO_BEGIN_DECLS @@ -55,7 +55,7 @@ CAIRO_BEGIN_DECLS * * #cairo_bool_t is used for boolean values. Returns of type * #cairo_bool_t will always be either 0 or 1, but testing against - * these values explicitely is not encouraged; just use the + * these values explicitly is not encouraged; just use the * value as a boolean condition. * * <informalexample><programlisting> @@ -94,9 +94,62 @@ typedef struct _cairo_surface cairo_surface_t; * A #cairo_matrix_t holds an affine transformation, such as a scale, * rotation, or shear, or a combination of those. **/ -typedef struct _cairo_matrix cairo_matrix_t; +typedef struct _cairo_matrix { + double xx; double yx; + double xy; double yy; + double x0; double y0; +} cairo_matrix_t; + typedef struct _cairo_pattern cairo_pattern_t; +/** + * cairo_destroy_func_t + * + * #cairo_destroy_func_t the type of function which is called when a + * data element is destroyed. It is passed the pointer to the data + * element and should free any memory and resources allocated for it. + */ +typedef void (*cairo_destroy_func_t) (void *data); + +/** + * cairo_user_data_key_t + * + * #cairo_user_data_key_t is used for attaching user data to cairo + * data structures. The actual contents of the struct is never used, + * and there is no need to initialize the object; only the unique + * address of a #cairo_data_key_t object is used. Typically, you + * would just use the address of a static #cairo_data_key_t object. + */ +typedef struct _cairo_user_data_key { + int unused; +} cairo_user_data_key_t; + +/** + * cairo_status_t + * @CAIRO_STATUS_SUCCESS: no error has occurred + * @CAIRO_STATUS_NO_MEMORY: + * @CAIRO_STATUS_INVALID_RESTORE: + * @CAIRO_STATUS_INVALID_POP_GROUP: + * @CAIRO_STATUS_INVALID_MATRIX: + * @CAIRO_STATUS_NO_TARGET_SURFACE: + * @CAIRO_STATUS_NULL_POINTER: + * @CAIRO_STATUS_INVALID_STRING: + * @CAIRO_STATUS_INVALID_PATH_DATA: + * @CAIRO_STATUS_READ_ERROR: + * @CAIRO_STATUS_WRITE_ERROR: + * @CAIRO_STATUS_SURFACE_FINISHED: + * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + * @CAIRO_STATUS_BAD_NESTING: the same surface was used as the + * target surface for two different cairo contexts at once, + * and more drawing was done on the first context before the + * surface was unset as the target for the second context. + * See the documentation for cairo_create(). + * + * #cairo_status_t is used to indicate errors that can occur when + * using Cairo. In some cases it is returned directly by functions. + * but when using #cairo_t, the last error, if any, is stored in + * the context and can be retrieved with cairo_status(). + **/ typedef enum cairo_status { CAIRO_STATUS_SUCCESS = 0, CAIRO_STATUS_NO_MEMORY, @@ -106,12 +159,48 @@ typedef enum cairo_status { CAIRO_STATUS_INVALID_MATRIX, CAIRO_STATUS_NO_TARGET_SURFACE, CAIRO_STATUS_NULL_POINTER, - CAIRO_STATUS_INVALID_STRING + CAIRO_STATUS_INVALID_STRING, + CAIRO_STATUS_INVALID_PATH_DATA, + CAIRO_STATUS_READ_ERROR, + CAIRO_STATUS_WRITE_ERROR, + CAIRO_STATUS_SURFACE_FINISHED, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH, + CAIRO_STATUS_BAD_NESTING } cairo_status_t; +/** + * cairo_write_func_t + * + * #cairo_write_func_t is the type of function which is called when a + * backend needs to write data to an output stream. It is passed the + * closure which was specified by the user at the time the write + * function was registered, the data to write and the length of the + * data in bytes. The write function should return + * CAIRO_STATUS_SUCCESS if all the data was successfully written, + * CAIRO_STATUS_WRITE_ERROR otherwise. + */ +typedef cairo_status_t (*cairo_write_func_t) (void *closure, + const unsigned char *data, + unsigned int length); + +/** + * cairo_read_func_t + * + * #cairo_read_func_t is the type of function which is called when a + * backend needs to read data from an intput stream. It is passed the + * closure which was specified by the user at the time the read + * function was registered, the buffer to read the data into and the + * length of the data in bytes. The read function should return + * CAIRO_STATUS_SUCCESS if all the data was successfully written, + * CAIRO_STATUS_READ_ERROR otherwise. + */ +typedef cairo_status_t (*cairo_read_func_t) (void *closure, + unsigned char *data, + unsigned int length); + /* Functions for manipulating state objects */ cairo_t * -cairo_create (void); +cairo_create (cairo_surface_t *target); void cairo_reference (cairo_t *cr); @@ -125,10 +214,6 @@ cairo_save (cairo_t *cr); void cairo_restore (cairo_t *cr); -/* XXX: Replace with cairo_current_gstate/cairo_set_gstate */ -void -cairo_copy (cairo_t *dest, cairo_t *src); - /* XXX: I want to rethink this API void cairo_push_group (cairo_t *cr); @@ -138,59 +223,22 @@ cairo_pop_group (cairo_t *cr); */ /* Modify state */ -void -cairo_set_target_surface (cairo_t *cr, cairo_surface_t *surface); - -/** - * cairo_format_t - * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with - * alpha in the upper 8 bits, then red, then green, then blue. - * The 32-bit quanties are stored native-endian. Pre-multiplied - * alpha is used. (That is, 50% transparent red is 0x80800000, - * not 0x80ff0000.) - * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with - * the upper 8 bits unused. Red, Green, and Blue are stored - * in the remaining 24 bits in that order. - * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding - * an alpha value. - * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding - * an alpha value. Pixels are packed together into 32-bit - * quantities. The ordering of the bits matches the - * endianess of the platform. On a big-endian machine, the - * first pixel is in the uppermost bit, on a little-endian - * machine the first pixel is in the least-significant bit. - * - * #cairo_format_t is used to identify the memory format of - * image data. - */ -typedef enum cairo_format { - CAIRO_FORMAT_ARGB32, - CAIRO_FORMAT_RGB24, - CAIRO_FORMAT_A8, - CAIRO_FORMAT_A1 -} cairo_format_t; - -/* XXX: Need to add cairo_set_target_image_data */ -void -cairo_set_target_image (cairo_t *cr, - char *data, - cairo_format_t format, - int width, - int height, - int stride); -typedef enum cairo_operator { +typedef enum cairo_operator { CAIRO_OPERATOR_CLEAR, - CAIRO_OPERATOR_SRC, - CAIRO_OPERATOR_DST, + + CAIRO_OPERATOR_SOURCE, CAIRO_OPERATOR_OVER, - CAIRO_OPERATOR_OVER_REVERSE, CAIRO_OPERATOR_IN, - CAIRO_OPERATOR_IN_REVERSE, CAIRO_OPERATOR_OUT, - CAIRO_OPERATOR_OUT_REVERSE, CAIRO_OPERATOR_ATOP, - CAIRO_OPERATOR_ATOP_REVERSE, + + CAIRO_OPERATOR_DEST, + CAIRO_OPERATOR_DEST_OVER, + CAIRO_OPERATOR_DEST_IN, + CAIRO_OPERATOR_DEST_OUT, + CAIRO_OPERATOR_DEST_ATOP, + CAIRO_OPERATOR_XOR, CAIRO_OPERATOR_ADD, CAIRO_OPERATOR_SATURATE @@ -199,36 +247,23 @@ typedef enum cairo_operator { void cairo_set_operator (cairo_t *cr, cairo_operator_t op); -/* XXX: Probably want to bite the bullet and expose a cairo_color_t object */ - -/* XXX: I've been trying to come up with a sane way to specify: - - cairo_set_color (cairo_t *cr, cairo_color_t *color); - - Keith wants to be able to support super-luminescent colors, - (premultiplied colors with R/G/B greater than alpha). The current - API does not allow that. Adding a premulitplied RGBA cairo_color_t - would do the trick. - - One problem though is that alpha is currently orthogonal to - color. For example, show_surface uses gstate->alpha but ignores the - color. So, it doesn't seem be right to have cairo_set_color modify - the behavior of cairo_show_surface. -*/ +void +cairo_set_source (cairo_t *cr, cairo_pattern_t *source); void -cairo_set_rgb_color (cairo_t *cr, double red, double green, double blue); +cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue); void -cairo_set_pattern (cairo_t *cr, cairo_pattern_t *pattern); +cairo_set_source_rgba (cairo_t *cr, + double red, double green, double blue, + double alpha); void -cairo_set_alpha (cairo_t *cr, double alpha); +cairo_set_source_surface (cairo_t *cr, + cairo_surface_t *surface, + double x, + double y); -/* XXX: Currently, the tolerance value is specified by the user in - terms of device-space units. If I'm not mistaken, this is the only - value in this API that is not expressed in user-space units. I - should think whether this value should be user-space instead. */ void cairo_set_tolerance (cairo_t *cr, double tolerance); @@ -307,32 +342,27 @@ void cairo_rotate (cairo_t *cr, double angle); void -cairo_concat_matrix (cairo_t *cr, - cairo_matrix_t *matrix); - -void -cairo_set_matrix (cairo_t *cr, - cairo_matrix_t *matrix); +cairo_transform (cairo_t *cr, + const cairo_matrix_t *matrix); void -cairo_default_matrix (cairo_t *cr); - -/* XXX: There's been a proposal to add cairo_default_matrix_exact */ +cairo_set_matrix (cairo_t *cr, + const cairo_matrix_t *matrix); void cairo_identity_matrix (cairo_t *cr); void -cairo_transform_point (cairo_t *cr, double *x, double *y); +cairo_user_to_device (cairo_t *cr, double *x, double *y); void -cairo_transform_distance (cairo_t *cr, double *dx, double *dy); +cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy); void -cairo_inverse_transform_point (cairo_t *cr, double *x, double *y); +cairo_device_to_user (cairo_t *cr, double *x, double *y); void -cairo_inverse_transform_distance (cairo_t *cr, double *dx, double *dy); +cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy); /* Path creation functions */ void @@ -387,16 +417,9 @@ cairo_rectangle (cairo_t *cr, double x, double y, double width, double height); -/* XXX: This is the same name that PostScript uses, but to me the name - suggests an actual drawing operation ala cairo_stroke --- especially - since I want to add a cairo_path_t and with that it would be - natural to have "cairo_stroke_path (cairo_t *, cairo_path_t *)" - - Maybe we could use something like "cairo_outline_path (cairo_t *)"? -*/ /* XXX: NYI void -cairo_stroke_path (cairo_t *cr); +cairo_stroke_to_path (cairo_t *cr); */ void @@ -404,12 +427,35 @@ cairo_close_path (cairo_t *cr); /* Painting functions */ void +cairo_paint (cairo_t *cr); + +void +cairo_paint_with_alpha (cairo_t *cr, + double alpha); + +void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern); + +void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y); + +void cairo_stroke (cairo_t *cr); void +cairo_stroke_preserve (cairo_t *cr); + +void cairo_fill (cairo_t *cr); void +cairo_fill_preserve (cairo_t *cr); + +void cairo_copy_page (cairo_t *cr); void @@ -435,25 +481,37 @@ cairo_fill_extents (cairo_t *cr, /* Clipping */ void -cairo_init_clip (cairo_t *cr); +cairo_reset_clip (cairo_t *cr); -/* Note: cairo_clip does not consume the current path */ void cairo_clip (cairo_t *cr); +void +cairo_clip_preserve (cairo_t *cr); + /* Font/Text functions */ /** - * cairo_font_t: - * - * A #cairo_font_t is a font scaled to a particular size and device - * resolution. A font can be set on a #cairo_t by using - * cairo_set_font() assuming that the current transformation and - * target surface of the #cairo_t match that for which the - * #cairo_font_t was created. The effect of using a mismatched - * #cairo_font_t will be incorrect font metrics. + * cairo_scaled_font_t: + * + * A #cairo_scaled_font_t is a font scaled to a particular size and device + * resolution. A cairo_scaled_font_t is most useful for low-level font + * usage where a library or application wants to cache a reference + * to a scaled font to speed up the computation of metrics. */ -typedef struct _cairo_font cairo_font_t; +typedef struct _cairo_scaled_font cairo_scaled_font_t; + +/** + * cairo_font_face_t: + * + * A #cairo_font_face_t specifies all aspects of a font other + * than the size or font matrix (a font matrix is used to distort + * a font by sheering it or scaling it unequally in the two + * directions) . A font face can be set on a #cairo_t by using + * cairo_set_font_face(); the size and font matrix are set with + * cairo_set_font_size() and cairo_set_font_matrix(). + */ +typedef struct _cairo_font_face cairo_font_face_t; /** * cairo_glyph_t: @@ -502,10 +560,10 @@ typedef struct { * after drawing these glyphs. Will typically be zero except * for vertical text layout as found in East-Asian languages. * - * The #cairo_text_extents_t< structure stores the extents of a single + * The #cairo_text_extents_t structure stores the extents of a single * glyph or a string of glyphs in user-space coordinates. Because text - * extents are in user-space coordinates, they don't scale along with - * the current transformation matrix. If you call + * extents are in user-space coordinates, they are mostly, but not + * entirely, independent of the current transformation matrix. If you call * <literal>cairo_scale(cr, 2.0, 2.0)</literal>, text will * be drawn twice as big, but the reported text extents will not be * doubled. They will change slightly due to hinting (so you can't @@ -521,6 +579,47 @@ typedef struct { double y_advance; } cairo_text_extents_t; +/** + * cairo_font_extents_t: + * @ascent: the distance that the font extends above the baseline. + * Note that this is not always exactly equal to the maximum + * of the extents of all the glyphs in the font, but rather + * is picked to express the font designer's intent as to + * how the font should align with elements above it. + * @descent: the distance that the font extends below the baseline. + * This value is positive for typical fonts that include + * portions below the baseline. Note that this is not always + * exactly equal to the maximum of the extents of all the + * glyphs in the font, but rather is picked to express the + * font designer's intent as to how the the font should + * align with elements below it. + * @height: the recommended vertical distance between baselines when + * setting consecutive lines of text with the font. This + * is greater than @ascent+@descent by a + * quantity known as the <firstterm>line spacing</firstterm> + * or <firstterm>external leading</firstterm>. When space + * is at a premium, most fonts can be set with only + * a distance of @ascent+@descent between lines. + * @max_x_advance: the maximum distance in the X direction that + * the the origin is advanced for any glyph in the font. + * @max_y_advance: the maximum distance in the Y direction that + * the the origin is advanced for any glyph in the font. + * this will be zero for normal fonts used for horizontal + * writing. (The scripts of East Asia are sometimes written + * vertically.) + * + * The #cairo_text_extents_t structure stores metric information for + * a font. Values are given in the current user-space coordinate + * system. + * + * Because font metrics are in user-space coordinates, they are + * mostly, but not entirely, independent of the current transformation + * matrix. If you call <literal>cairo_scale(cr, 2.0, 2.0)</literal>, + * text will be drawn twice as big, but the reported text extents will + * not be doubled. They will change slightly due to hinting (so you + * can't assume that metrics are independent of the transformation + * matrix), but otherwise will remain unchanged. + */ typedef struct { double ascent; double descent; @@ -544,37 +643,42 @@ typedef enum cairo_font_weight { font object inside the the cairo_t. */ void -cairo_select_font (cairo_t *cr, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); +cairo_select_font_face (cairo_t *cr, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + +void +cairo_set_font_size (cairo_t *cr, double size); void -cairo_scale_font (cairo_t *cr, double scale); +cairo_set_font_matrix (cairo_t *cr, + const cairo_matrix_t *matrix); void -cairo_transform_font (cairo_t *cr, cairo_matrix_t *matrix); +cairo_get_font_matrix (cairo_t *cr, + cairo_matrix_t *matrix); void -cairo_show_text (cairo_t *cr, const unsigned char *utf8); +cairo_show_text (cairo_t *cr, const char *utf8); void cairo_show_glyphs (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs); -cairo_font_t * -cairo_current_font (cairo_t *cr); +cairo_font_face_t * +cairo_get_font_face (cairo_t *cr); void -cairo_current_font_extents (cairo_t *cr, - cairo_font_extents_t *extents); +cairo_font_extents (cairo_t *cr, + cairo_font_extents_t *extents); void -cairo_set_font (cairo_t *cr, cairo_font_t *font); +cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face); void -cairo_text_extents (cairo_t *cr, - const unsigned char *utf8, - cairo_text_extents_t *extents); +cairo_text_extents (cairo_t *cr, + const char *utf8, + cairo_text_extents_t *extents); void cairo_glyph_extents (cairo_t *cr, @@ -583,119 +687,197 @@ cairo_glyph_extents (cairo_t *cr, cairo_text_extents_t *extents); void -cairo_text_path (cairo_t *cr, const unsigned char *utf8); +cairo_text_path (cairo_t *cr, const char *utf8); void cairo_glyph_path (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs); -/* Portable interface to general font features. */ - +/* Generic identifier for a font style */ + void -cairo_font_reference (cairo_font_t *font); +cairo_font_face_reference (cairo_font_face_t *font_face); void -cairo_font_destroy (cairo_font_t *font); +cairo_font_face_destroy (cairo_font_face_t *font_face); + +void * +cairo_font_face_get_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key); cairo_status_t -cairo_font_extents (cairo_font_t *font, - cairo_matrix_t *font_matrix, - cairo_font_extents_t *extents); +cairo_font_face_set_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +/* Portable interface to general font features. */ + +cairo_scaled_font_t * +cairo_scaled_font_create (cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm); void -cairo_font_glyph_extents (cairo_font_t *font, - cairo_matrix_t *font_matrix, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); +cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font); -/* Image functions */ +void +cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font); + +cairo_status_t +cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); -/* XXX: Eliminate width/height here */ void -cairo_show_surface (cairo_t *cr, - cairo_surface_t *surface, - int width, - int height); +cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); /* Query functions */ -/* XXX: It would be nice if I could find a simpler way to make the - definitions for the deprecated functions. Ideally, I would just - have to put DEPRECATE (cairo_get_operator, cairo_current_operator) - into one file and be done with it. For now, I've got a little more - typing than that. */ - cairo_operator_t -cairo_current_operator (cairo_t *cr); +cairo_get_operator (cairo_t *cr); -void -cairo_current_rgb_color (cairo_t *cr, double *red, double *green, double *blue); cairo_pattern_t * -cairo_current_pattern (cairo_t *cr); - -double -cairo_current_alpha (cairo_t *cr); - -/* XXX: Do we want cairo_current_pattern as well? */ +cairo_get_source (cairo_t *cr); double -cairo_current_tolerance (cairo_t *cr); +cairo_get_tolerance (cairo_t *cr); void -cairo_current_point (cairo_t *cr, double *x, double *y); +cairo_get_current_point (cairo_t *cr, double *x, double *y); cairo_fill_rule_t -cairo_current_fill_rule (cairo_t *cr); +cairo_get_fill_rule (cairo_t *cr); double -cairo_current_line_width (cairo_t *cr); +cairo_get_line_width (cairo_t *cr); cairo_line_cap_t -cairo_current_line_cap (cairo_t *cr); +cairo_get_line_cap (cairo_t *cr); cairo_line_join_t -cairo_current_line_join (cairo_t *cr); +cairo_get_line_join (cairo_t *cr); double -cairo_current_miter_limit (cairo_t *cr); +cairo_get_miter_limit (cairo_t *cr); -/* XXX: How to do cairo_current_dash??? Do we want to switch to a cairo_dash object? */ +/* XXX: How to do cairo_get_dash??? Do we want to switch to a cairo_dash object? */ void -cairo_current_matrix (cairo_t *cr, cairo_matrix_t *matrix); +cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix); -/* XXX: Need to decide the memory mangement semantics of this +/* XXX: Need to decide the memory management semantics of this function. Should it reference the surface again? */ cairo_surface_t * -cairo_current_target_surface (cairo_t *cr); +cairo_get_target (cairo_t *cr); -typedef void (cairo_move_to_func_t) (void *closure, - double x, double y); +/** + * cairo_path_data_t: + * + * A data structure for holding path data---appears within + * #cairo_path_t. + * + * The data structure is designed to try to balance the demands of + * efficiency and ease-of-use. A path is represented as an array of + * cairo_path_data_t which is a union of headers and points. + * + * Each portion of the path is represented by one or more elements in + * the array, (one header followed by 0 or more points). The length + * value of the header is the number of array elements for the current + * portion including the header, (ie. length == 1 + # of points), and + * where the number of points for each element type must be as + * follows: + * + * CAIRO_PATH_MOVE_TO: 1 point + * CAIRO_PATH_LINE_TO: 1 point + * CAIRO_PATH_CURVE_TO: 3 points + * CAIRO_PATH_CLOSE_PATH: 0 points + * + * The semantics and ordering of the coordinate values are consistent + * with cairo_move_to(), cairo_line_to(), cairo_curve_to(), and + * cairo_close_path(). + * + * Here is sample code for iterating through a #cairo_path_t: + * + * <informalexample><programlisting> + * int i; + * cairo_path_t *path; + * cairo_path_data_t *data; + * + * path = cairo_copy_path (cr); + * + * for (i=0; i < path->num_data; i += path->data[i].header.length) { + * data = &path->data[i]; + * switch (data->header.type) { + * case CAIRO_PATH_MOVE_TO: + * do_move_to_things (data[1].point.x, data[1].point.y); + * break; + * case CAIRO_PATH_LINE_TO: + * do_line_to_things (data[1].point.x, data[1].point.y); + * break; + * case CAIRO_PATH_CURVE_TO: + * do_curve_to_things (data[1].point.x, data[1].point.y, + * data[2].point.x, data[2].point.y, + * data[3].point.x, data[3].point.y); + * break; + * case CAIRO_PATH_CLOSE_PATH: + * do_close_path_things (); + * break; + * } + * } + * + * cairo_path_destroy (path); + * </programlisting></informalexample> + */ +typedef union { + struct { + enum { + CAIRO_PATH_MOVE_TO, + CAIRO_PATH_LINE_TO, + CAIRO_PATH_CURVE_TO, + CAIRO_PATH_CLOSE_PATH + } type; + int length; + } header; + struct { + double x, y; + } point; +} cairo_path_data_t; -typedef void (cairo_line_to_func_t) (void *closure, - double x, double y); +/** + * cairo_path_t: + * + * A data structure for holding a path. This data structure serves as + * the return value for cairo_copy_path_data() and + * cairo_copy_path_data_flat() as well the input value for + * cairo_append_path(). + * + * See #cairo_path_data_t for hints on how to iterate over the + * actual data within the path. + * + * The num_data member gives the number of elements in the data + * array. This number is larger than the number of independent path + * portions (MOVE_TO, LINE_TO, CURVE_TO, CLOSE_PATH), since the data + * includes both headers and coordinates for each portion. + **/ +typedef struct cairo_path { + cairo_path_data_t *data; + int num_data; +} cairo_path_t; -typedef void (cairo_curve_to_func_t) (void *closure, - double x1, double y1, - double x2, double y2, - double x3, double y3); +cairo_path_t * +cairo_copy_path (cairo_t *cr); -typedef void (cairo_close_path_func_t) (void *closure); +cairo_path_t * +cairo_copy_path_flat (cairo_t *cr); -extern void -cairo_current_path (cairo_t *cr, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_curve_to_func_t *curve_to, - cairo_close_path_func_t *close_path, - void *closure); +void +cairo_append_path (cairo_t *cr, + cairo_path_t *path); -extern void -cairo_current_path_flat (cairo_t *cr, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_close_path_func_t *close_path, - void *closure); +void +cairo_path_destroy (cairo_path_t *path); /* Error status queries */ @@ -706,17 +888,38 @@ const char * cairo_status_string (cairo_t *cr); /* Surface manipulation */ -/* XXX: We may want to rename this function in light of the new - virtualized surface backends... */ -cairo_surface_t * -cairo_surface_create_for_image (char *data, - cairo_format_t format, - int width, - int height, - int stride); + +/** + * cairo_format_t + * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with + * alpha in the upper 8 bits, then red, then green, then blue. + * The 32-bit quantities are stored native-endian. Pre-multiplied + * alpha is used. (That is, 50% transparent red is 0x80800000, + * not 0x80ff0000.) + * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with + * the upper 8 bits unused. Red, Green, and Blue are stored + * in the remaining 24 bits in that order. + * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding + * an alpha value. + * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding + * an alpha value. Pixels are packed together into 32-bit + * quantities. The ordering of the bits matches the + * endianess of the platform. On a big-endian machine, the + * first pixel is in the uppermost bit, on a little-endian + * machine the first pixel is in the least-significant bit. + * + * #cairo_format_t is used to identify the memory format of + * image data. + */ +typedef enum cairo_format { + CAIRO_FORMAT_ARGB32, + CAIRO_FORMAT_RGB24, + CAIRO_FORMAT_A8, + CAIRO_FORMAT_A1 +} cairo_format_t; /* XXX: I want to remove this function, (replace with - cairo_set_target_scratch or similar). */ + cairo_begin_group and friends). */ cairo_surface_t * cairo_surface_create_similar (cairo_surface_t *other, cairo_format_t format, @@ -729,35 +932,36 @@ cairo_surface_reference (cairo_surface_t *surface); void cairo_surface_destroy (cairo_surface_t *surface); -/* XXX: Note: The current Render/Ic implementations don't do the right - thing with repeat when the surface has a non-identity matrix. */ -/* XXX: Rework this as a cairo function with an enum: cairo_set_pattern_extend */ cairo_status_t -cairo_surface_set_repeat (cairo_surface_t *surface, int repeat); +cairo_surface_finish (cairo_surface_t *surface); + +#if CAIRO_HAS_PNG_FUNCTIONS -/* XXX: Rework this as a cairo function: cairo_set_pattern_transform */ cairo_status_t -cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix); +cairo_surface_write_to_png (cairo_surface_t *surface, + const char *filename); -/* XXX: Rework this as a cairo function: cairo_current_pattern_transform */ cairo_status_t -cairo_surface_get_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix); +cairo_surface_write_to_png_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure); + +#endif + +void * +cairo_surface_get_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key); -typedef enum { - CAIRO_FILTER_FAST, - CAIRO_FILTER_GOOD, - CAIRO_FILTER_BEST, - CAIRO_FILTER_NEAREST, - CAIRO_FILTER_BILINEAR, - CAIRO_FILTER_GAUSSIAN -} cairo_filter_t; - -/* XXX: Rework this as a cairo function: cairo_set_pattern_filter */ cairo_status_t -cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter); +cairo_surface_set_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); -cairo_filter_t -cairo_surface_get_filter (cairo_surface_t *surface); +void +cairo_surface_set_device_offset (cairo_surface_t *surface, + double x_offset, + double y_offset); /* Image-surface functions */ @@ -767,12 +971,29 @@ cairo_image_surface_create (cairo_format_t format, int height); cairo_surface_t * -cairo_image_surface_create_for_data (char *data, +cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride); +int +cairo_image_surface_get_width (cairo_surface_t *surface); + +int +cairo_image_surface_get_height (cairo_surface_t *surface); + +#if CAIRO_HAS_PNG_FUNCTIONS + +cairo_surface_t * +cairo_image_surface_create_from_png (const char *filename); + +cairo_surface_t * +cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, + void *closure); + +#endif + /* Pattern creation functions */ cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface); @@ -792,16 +1013,23 @@ void cairo_pattern_destroy (cairo_pattern_t *pattern); cairo_status_t -cairo_pattern_add_color_stop (cairo_pattern_t *pattern, - double offset, - double red, double green, double blue, - double alpha); - +cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue); + cairo_status_t -cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix); +cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue, + double alpha); cairo_status_t -cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix); +cairo_pattern_set_matrix (cairo_pattern_t *pattern, + const cairo_matrix_t *matrix); + +cairo_status_t +cairo_pattern_get_matrix (cairo_pattern_t *pattern, + cairo_matrix_t *matrix); typedef enum { CAIRO_EXTEND_NONE, @@ -815,6 +1043,15 @@ cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); cairo_extend_t cairo_pattern_get_extend (cairo_pattern_t *pattern); +typedef enum { + CAIRO_FILTER_FAST, + CAIRO_FILTER_GOOD, + CAIRO_FILTER_BEST, + CAIRO_FILTER_NEAREST, + CAIRO_FILTER_BILINEAR, + CAIRO_FILTER_GAUSSIAN +} cairo_filter_t; + cairo_status_t cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter); @@ -823,72 +1060,135 @@ cairo_pattern_get_filter (cairo_pattern_t *pattern); /* Matrix functions */ -/* XXX: Rename all of these to cairo_transform_t */ - -cairo_matrix_t * -cairo_matrix_create (void); - void -cairo_matrix_destroy (cairo_matrix_t *matrix); +cairo_matrix_init (cairo_matrix_t *matrix, + double xx, double yx, + double xy, double yy, + double x0, double y0); -cairo_status_t -cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other); +void +cairo_matrix_init_identity (cairo_matrix_t *matrix); -cairo_status_t -cairo_matrix_set_identity (cairo_matrix_t *matrix); +void +cairo_matrix_init_translate (cairo_matrix_t *matrix, + double tx, double ty); -cairo_status_t -cairo_matrix_set_affine (cairo_matrix_t *matrix, - double a, double b, - double c, double d, - double tx, double ty); +void +cairo_matrix_init_scale (cairo_matrix_t *matrix, + double sx, double sy); -cairo_status_t -cairo_matrix_get_affine (cairo_matrix_t *matrix, - double *a, double *b, - double *c, double *d, - double *tx, double *ty); +void +cairo_matrix_init_rotate (cairo_matrix_t *matrix, + double radians); -cairo_status_t +void cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); -cairo_status_t +void cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); -cairo_status_t +void cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix); -cairo_status_t -cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b); +void +cairo_matrix_multiply (cairo_matrix_t *result, + const cairo_matrix_t *a, + const cairo_matrix_t *b); -cairo_status_t -cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy); +/* XXX: Need a new name here perhaps. */ +void +cairo_matrix_transform_distance (const cairo_matrix_t *matrix, + double *dx, double *dy); -cairo_status_t -cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y); +/* XXX: Need a new name here perhaps. */ +void +cairo_matrix_transform_point (const cairo_matrix_t *matrix, + double *x, double *y); -/* Deprecated functions. We've made some effort to allow the - deprecated functions to continue to work for now, (with useful - warnings). But the deprecated functions will not appear in the next - release. */ #ifndef _CAIROINT_H_ -#define cairo_get_operator cairo_get_operator_DEPRECATED_BY_cairo_current_operator -#define cairo_get_rgb_color cairo_get_rgb_color_DEPRECATED_BY_cairo_current_rgb_color -#define cairo_get_alpha cairo_get_alpha_DEPRECATED_BY_cairo_current_alpha -#define cairo_get_tolerance cairo_get_tolerance_DEPRECATED_BY_cairo_current_tolerance -#define cairo_get_current_point cairo_get_current_point_DEPRECATED_BY_cairo_current_point -#define cairo_get_fill_rule cairo_get_fill_rule_DEPRECATED_BY_cairo_current_fill_rule -#define cairo_get_line_width cairo_get_line_width_DEPRECATED_BY_cairo_current_line_width -#define cairo_get_line_cap cairo_get_line_cap_DEPRECATED_BY_cairo_current_line_cap -#define cairo_get_line_join cairo_get_line_join_DEPRECATED_BY_cairo_current_line_join -#define cairo_get_miter_limit cairo_get_miter_limit_DEPRECATED_BY_cairo_current_miter_limit -#define cairo_get_matrix cairo_get_matrix_DEPRECATED_BY_cairo_current_matrix -#define cairo_get_target_surface cairo_get_target_surface_DEPRECATED_BY_cairo_current_target_surface -#define cairo_get_status cairo_get_status_DEPRECATED_BY_cairo_status -#define cairo_get_status_string cairo_get_status_string_DEPRECATED_BY_cairo_status_string + +/* Obsolete functions. These definitions exist to coerce the compiler + * into providing a little bit of guidance with its error + * messages. The idea is to help users port their old code without + * having to dig through lots of documentation. + * + * The first set of REPLACED_BY functions is for functions whose names + * have just been changed. So fixing these up is mechanical, (and + * automated by means of the cairo/util/cairo-api-update script. + * + * The second set of DEPRECATED_BY functions is for functions where + * the replacement is used in a different way, (ie. different + * arguments, multiple functions instead of one, etc). Fixing these up + * will require a bit more work on the user's part, (and hopefully we + * can get cairo-api-update to find these and print some guiding + * information). + */ +#define cairo_current_font_extents cairo_current_font_extents_REPLACED_BY_cairo_font_extents +#define cairo_get_font_extents cairo_get_font_extents_REPLACED_BY_cairo_font_extents +#define cairo_current_operator cairo_current_operator_REPLACED_BY_cairo_get_operator +#define cairo_current_tolerance cairo_current_tolerance_REPLACED_BY_cairo_get_tolerance +#define cairo_current_point cairo_current_point_REPLACED_BY_cairo_get_current_point +#define cairo_current_fill_rule cairo_current_fill_rule_REPLACED_BY_cairo_get_fill_rule +#define cairo_current_line_width cairo_current_line_width_REPLACED_BY_cairo_get_line_width +#define cairo_current_line_cap cairo_current_line_cap_REPLACED_BY_cairo_get_line_cap +#define cairo_current_line_join cairo_current_line_join_REPLACED_BY_cairo_get_line_join +#define cairo_current_miter_limit cairo_current_miter_limit_REPLACED_BY_cairo_get_miter_limit +#define cairo_current_matrix cairo_current_matrix_REPLACED_BY_cairo_get_matrix +#define cairo_current_target_surface cairo_current_target_surface_REPLACED_BY_cairo_get_target +#define cairo_get_status cairo_get_status_REPLACED_BY_cairo_status +#define cairo_get_status_string cairo_get_status_string_REPLACED_BY_cairo_status_string +#define cairo_concat_matrix cairo_concat_matrix_REPLACED_BY_cairo_transform +#define cairo_scale_font cairo_scale_font_REPLACED_BY_cairo_set_font_size +#define cairo_select_font cairo_select_font_REPLACED_BY_cairo_select_font_face +#define cairo_transform_font cairo_transform_font_REPLACED_BY_cairo_set_font_matrix +#define cairo_transform_point cairo_transform_point_REPLACED_BY_cairo_user_to_device +#define cairo_transform_distance cairo_transform_distance_REPLACED_BY_cairo_user_to_device_distance +#define cairo_inverse_transform_point cairo_inverse_transform_point_REPLACED_BY_cairo_device_to_user +#define cairo_inverse_transform_distance cairo_inverse_transform_distance_REPLACED_BY_cairo_device_to_user_distance +#define cairo_init_clip cairo_init_clip_REPLACED_BY_cairo_reset_clip +#define cairo_surface_create_for_image cairo_surface_create_for_image_REPLACED_BY_cairo_image_surface_create_for_data +#define cairo_default_matrix cairo_default_matrix_REPLACED_BY_cairo_identity_matrix +#define cairo_matrix_set_affine cairo_matrix_set_affine_REPLACED_BY_cairo_matrix_init +#define cairo_matrix_set_identity cairo_matrix_set_identity_REPLACED_BY_cairo_matrix_init_identity +#define cairo_pattern_add_color_stop cairo_pattern_add_color_stop_REPLACED_BY_cairo_pattern_add_color_stop_rgba +#define cairo_set_rgb_color cairo_set_rgb_color_REPLACED_BY_cairo_set_source_rgb +#define cairo_set_pattern cairo_set_pattern_REPLACED_BY_cairo_set_source +#define cairo_xlib_surface_create_for_pixmap_with_visual cairo_xlib_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xlib_surface_create +#define cairo_xlib_surface_create_for_window_with_visual cairo_xlib_surface_create_for_window_with_visual_REPLACED_BY_cairo_xlib_surface_create +#define cairo_xcb_surface_create_for_pixmap_with_visual cairo_xcb_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xcb_surface_create +#define cairo_xcb_surface_create_for_window_with_visual cairo_xcb_surface_create_for_window_with_visual_REPLACED_BY_cairo_xcb_surface_create + + +#define cairo_current_path cairo_current_path_DEPRECATED_BY_cairo_copy_path +#define cairo_current_path_flat cairo_current_path_flat_DEPRECATED_BY_cairo_copy_path_flat +#define cairo_get_path cairo_get_path_DEPRECATED_BY_cairo_copy_path +#define cairo_get_path_flat cairo_get_path_flat_DEPRECATED_BY_cairo_get_path_flat +#define cairo_set_alpha cairo_set_alpha_DEPRECATED_BY_cairo_set_source_rgba_OR_cairo_paint_with_alpha +#define cairo_show_surface cairo_show_surface_DEPRECATED_BY_cairo_set_source_surface_AND_cairo_paint +#define cairo_copy cairo_copy_DEPRECATED_BY_cairo_create_AND_MANY_INDIVIDUAL_FUNCTIONS +#define cairo_surface_set_repeat cairo_surface_set_repeat_DEPRECATED_BY_cairo_pattern_set_extend +#define cairo_surface_set_matrix cairo_surface_set_matrix_DEPRECATED_BY_cairo_pattern_set_matrix +#define cairo_surface_get_matrix cairo_surface_get_matrix_DEPRECATED_BY_cairo_pattern_get_matrix +#define cairo_surface_set_filter cairo_surface_set_filter_DEPRECATED_BY_cairo_pattern_set_filter +#define cairo_surface_get_filter cairo_surface_get_filter_DEPRECATED_BY_cairo_pattern_get_filter +#define cairo_matrix_create cairo_matrix_create_DEPRECATED_BY_cairo_matrix_t +#define cairo_matrix_destroy cairo_matrix_destroy_DEPRECATED_BY_cairo_matrix_t +#define cairo_matrix_copy cairo_matrix_copy_DEPRECATED_BY_cairo_matrix_t +#define cairo_matrix_get_affine cairo_matrix_get_affine_DEPRECATED_BY_cairo_matrix_t +#define cairo_set_target_surface cairo_set_target_surface_DEPRECATED_BY_cairo_create +#define cairo_set_target_glitz cairo_set_target_glitz_DEPRECATED_BY_cairo_glitz_surface_create +#define cairo_set_target_image cairo_set_target_image_DEPRECATED_BY_cairo_image_surface_create_for_data +#define cairo_set_target_pdf cairo_set_target_pdf_DEPRECATED_BY_cairo_pdf_surface_create +#define cairo_set_target_png cairo_set_target_png_DEPRECATED_BY_cairo_surface_write_to_png +#define cairo_set_target_ps cairo_set_target_ps_DEPRECATED_BY_cairo_ps_surface_create +#define cairo_set_target_quartz cairo_set_target_quartz_DEPRECATED_BY_cairo_quartz_surface_create +#define cairo_set_target_win32 cairo_set_target_win32_DEPRECATED_BY_cairo_win32_surface_create +#define cairo_set_target_xcb cairo_set_target_xcb_DEPRECATED_BY_cairo_xcb_surface_create +#define cairo_set_target_drawable cairo_set_target_drawable_DEPRECATED_BY_cairo_xlib_surface_create + #endif CAIRO_END_DECLS diff --git a/src/cairo_array.c b/src/cairo_array.c deleted file mode 100644 index 2b1cf9d61..000000000 --- a/src/cairo_array.c +++ /dev/null @@ -1,134 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 Red Hat, Inc - * - * 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Kristian Høgsberg <krh@redhat.com> - */ - -#include "cairoint.h" - -void -_cairo_array_init (cairo_array_t *array, int element_size) -{ - array->size = 0; - array->num_elements = 0; - array->element_size = element_size; - array->elements = NULL; -} - -void -_cairo_array_fini (cairo_array_t *array) -{ - free (array->elements); -} - -cairo_status_t -_cairo_array_grow_by (cairo_array_t *array, int additional) -{ - char *new_elements; - int old_size = array->size; - int required_size = array->num_elements + additional; - int new_size; - - if (required_size <= old_size) - return CAIRO_STATUS_SUCCESS; - - if (old_size == 0) - new_size = 1; - else - new_size = old_size * 2; - - while (new_size < required_size) - new_size = new_size * 2; - - array->size = new_size; - new_elements = realloc (array->elements, - array->size * array->element_size); - - if (new_elements == NULL) { - array->size = old_size; - return CAIRO_STATUS_NO_MEMORY; - } - - array->elements = new_elements; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_array_truncate (cairo_array_t *array, int num_elements) -{ - if (num_elements < array->num_elements) - array->num_elements = num_elements; -} - -void * -_cairo_array_index (cairo_array_t *array, int index) -{ - assert (0 <= index && index < array->num_elements); - - return (void *) &array->elements[index * array->element_size]; -} - -void -_cairo_array_copy_element (cairo_array_t *array, int index, void *dst) -{ - memcpy (dst, _cairo_array_index (array, index), array->element_size); -} - -void * -_cairo_array_append (cairo_array_t *array, - const void *elements, int num_elements) -{ - cairo_status_t status; - void *dest; - - status = _cairo_array_grow_by (array, num_elements); - if (status != CAIRO_STATUS_SUCCESS) - return NULL; - - assert (array->num_elements + num_elements <= array->size); - - dest = &array->elements[array->num_elements * array->element_size]; - array->num_elements += num_elements; - - if (elements != NULL) - memcpy (dest, elements, num_elements * array->element_size); - - return dest; -} - -int -_cairo_array_num_elements (cairo_array_t *array) -{ - return array->num_elements; -} diff --git a/src/cairo_atsui_font.c b/src/cairo_atsui_font.c deleted file mode 100644 index cb4b1c5d7..000000000 --- a/src/cairo_atsui_font.c +++ /dev/null @@ -1,807 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 Calum Robinson - * - * 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. - * - * The Initial Developer of the Original Code is Calum Robinson - * - * Contributor(s): - * Calum Robinson <calumr@mac.com> - */ - -#include <stdlib.h> -#include <math.h> - -#include "cairo-atsui.h" -#include "cairoint.h" - - - - - -#pragma mark Types - - - - - -typedef struct { - cairo_unscaled_font_t base; - - ATSUStyle style; - ATSUFontID fontID; -} cairo_atsui_font_t; - - -typedef struct cairo_ATSUI_glyph_path_callback_info_t { - cairo_path_t *path; - cairo_matrix_t scale; -} cairo_ATSUI_glyph_path_callback_info_t; - - - - - -#pragma mark Private Functions - - - - - -static CGAffineTransform CGAffineTransformMakeWithCairoFontScale(cairo_font_scale_t scale) -{ - return CGAffineTransformMake( scale.matrix[0][0], scale.matrix[0][1], - scale.matrix[1][0], scale.matrix[1][1], - 0, 0); -} - - -static ATSUStyle CreateSizedCopyOfStyle(ATSUStyle inStyle, cairo_font_scale_t *scale) -{ - ATSUStyle style; - OSStatus err; - - - // Set the style's size - CGAffineTransform theTransform = CGAffineTransformMakeWithCairoFontScale(*scale); - Fixed theSize = FloatToFixed(CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), theTransform).height); - const ATSUAttributeTag theFontStyleTags[] = { kATSUSizeTag }; - const ByteCount theFontStyleSizes[] = { sizeof(Fixed) }; - ATSUAttributeValuePtr theFontStyleValues[] = { &theSize }; - - err = ATSUCreateAndCopyStyle(inStyle, &style); - - err = ATSUSetAttributes( style, - sizeof(theFontStyleTags) / sizeof(ATSUAttributeTag), - theFontStyleTags, theFontStyleSizes, theFontStyleValues); - - - return style; -} - - - - - -#pragma mark Public Functions - - - - - -static cairo_unscaled_font_t * -_cairo_atsui_font_create( const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_atsui_font_t *font = NULL; - ATSUStyle style; - ATSUFontID fontID; - OSStatus err; - Boolean isItalic, isBold; - - - err = ATSUCreateStyle(&style); - - - switch (weight) - { - case CAIRO_FONT_WEIGHT_BOLD: - isBold = true; - break; - case CAIRO_FONT_WEIGHT_NORMAL: - default: - isBold = false; - break; - } - - switch (slant) - { - case CAIRO_FONT_SLANT_ITALIC: - isItalic = true; - break; - case CAIRO_FONT_SLANT_OBLIQUE: - isItalic = false; - break; - case CAIRO_FONT_SLANT_NORMAL: - default: - isItalic = false; - break; - } - - err = ATSUFindFontFromName( family, strlen(family), - kFontFamilyName, - kFontNoPlatformCode, - kFontRomanScript, - kFontNoLanguageCode, - &fontID); - - - ATSUAttributeTag styleTags[] = {kATSUQDItalicTag, kATSUQDBoldfaceTag, kATSUFontTag}; - ATSUAttributeValuePtr styleValues[] = {&isItalic, &isBold, &fontID}; - ByteCount styleSizes[] = {sizeof(Boolean), sizeof(Boolean), sizeof(ATSUFontID)}; - - - err = ATSUSetAttributes( style, - sizeof(styleTags) / sizeof(styleTags[0]), - styleTags, - styleSizes, - styleValues); - - - - font = malloc(sizeof(cairo_atsui_font_t)); - - if (_cairo_unscaled_font_init(&font->base, &cairo_atsui_font_backend) == CAIRO_STATUS_SUCCESS) - { - font->style = style; - font->fontID = fontID; - - - return &font->base; - } - - - free(font); - - return NULL; -} - - -static void -_cairo_atsui_font_destroy(void *abstract_font) -{ - cairo_atsui_font_t *font = abstract_font; - - - if (font == NULL) - return; - - if (font->style) - ATSUDisposeStyle(font->style); - - free(font); -} - - -static cairo_status_t -_cairo_atsui_font_text_to_glyphs( void *abstract_font, - cairo_font_scale_t *sc, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *nglyphs) -{ - cairo_atsui_font_t *font = abstract_font; - size_t i; - OSStatus err; - ATSUTextLayout textLayout; - ATSLayoutRecord *layoutRecords; - ItemCount glyphCount, charCount; - UniChar *theText; - ATSUStyle style; - - - charCount = strlen(utf8); - - - err = ATSUCreateTextLayout(&textLayout); - - - // Set the text in the text layout object, so we can measure it - theText = (UniChar *)malloc(charCount * sizeof(UniChar)); - - for (i = 0; i < charCount; i++) - { - theText[i] = utf8[i]; - } - - err = ATSUSetTextPointerLocation( textLayout, - theText, - 0, - charCount, - charCount); - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - // Set the style for all of the text - err = ATSUSetRunStyle( textLayout, - style, - kATSUFromTextBeginning, - kATSUToTextEnd); - - - - // Get the glyphs from the text layout object - err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout( textLayout, - 0, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void *)&layoutRecords, - &glyphCount); - - *nglyphs = glyphCount; - - - *glyphs = (cairo_glyph_t *)malloc(glyphCount * (sizeof(cairo_glyph_t))); - if (*glyphs == NULL) - { - return CAIRO_STATUS_NO_MEMORY; - } - - for (i = 0; i < glyphCount; i++) - { - (*glyphs)[i].index = layoutRecords[i].glyphID; - (*glyphs)[i].x = FixedToFloat(layoutRecords[i].realPos); - (*glyphs)[i].y = 0; - } - - - free(theText); - - ATSUDirectReleaseLayoutDataArrayPtr( NULL, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void *)&layoutRecords); - - ATSUDisposeTextLayout(textLayout); - - ATSUDisposeStyle(style); - - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_atsui_font_font_extents( void *abstract_font, - cairo_font_scale_t *sc, - cairo_font_extents_t *extents) -{ - cairo_atsui_font_t *font = abstract_font; - ATSFontRef atsFont; - ATSFontMetrics metrics; - OSStatus err; - - - // TODO - test this - - atsFont = FMGetATSFontRefFromFont(font->fontID); - - if (atsFont) - { - err = ATSFontGetHorizontalMetrics(atsFont, kATSOptionFlagsDefault, &metrics); - - if (err == noErr) - { - extents->ascent = metrics.ascent; - extents->descent = metrics.descent; - extents->height = metrics.capHeight; - extents->max_x_advance = metrics.maxAdvanceWidth; - - // The FT backend doesn't handle max_y_advance either, so we'll ignore it for now. - extents->max_y_advance = 0.0; - - - return CAIRO_STATUS_SUCCESS; - } - } - - - return CAIRO_STATUS_NULL_POINTER; -} - - -static cairo_status_t -_cairo_atsui_font_glyph_extents( void *abstract_font, - cairo_font_scale_t *sc, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_atsui_font_t *font = abstract_font; - cairo_point_double_t origin; - cairo_point_double_t glyph_min, glyph_max; - cairo_point_double_t total_min, total_max; - OSStatus err; - ATSUStyle style; - int i; - - - if (num_glyphs == 0) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return CAIRO_STATUS_SUCCESS; - } - - origin.x = glyphs[0].x; - origin.y = glyphs[0].y; - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - for (i = 0; i < num_glyphs; i++) - { - GlyphID theGlyph = glyphs[i].index; - double minX, maxX, ascent, descent; - ATSGlyphIdealMetrics metricsH, metricsV; - - - err = ATSUGlyphGetIdealMetrics( style, - 1, - &theGlyph, - 0, - &metricsH); - - - ATSUVerticalCharacterType verticalType = kATSUStronglyVertical; - ATSUAttributeTag theTag = kATSUVerticalCharacterTag; - ByteCount theSize = sizeof(ATSUVerticalCharacterType); - - err = ATSUSetAttributes(style, 1, &theTag, &theSize, (ATSUAttributeValuePtr)&verticalType); - - err = ATSUGlyphGetIdealMetrics( style, - 1, - &theGlyph, - 0, - &metricsV); - - minX = metricsH.otherSideBearing.x; - maxX = metricsH.advance.x; - - ascent = metricsV.advance.x; - descent = metricsV.otherSideBearing.x; - - glyph_min.x = glyphs[i].x + minX; - glyph_min.y = glyphs[i].y + descent; - glyph_max.x = glyphs[i].x + maxX; - glyph_max.y = glyphs[i].y + ascent; - - if (i==0) - { - total_min = glyph_min; - total_max = glyph_max; - } - else - { - if (glyph_min.x < total_min.x) - total_min.x = glyph_min.x; - if (glyph_min.y < total_min.y) - total_min.y = glyph_min.y; - - if (glyph_max.x > total_max.x) - total_max.x = glyph_max.x; - if (glyph_max.y > total_max.y) - total_max.y = glyph_max.y; - } - } - - - extents->x_bearing = total_min.x - origin.x; - extents->y_bearing = total_min.y - origin.y; - extents->width = total_max.x - total_min.x; - extents->height = total_max.y - total_min.y; - extents->x_advance = glyphs[i-1].x - origin.x; - extents->y_advance = glyphs[i-1].y - origin.y; - - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_atsui_font_glyph_bbox( void *abstract_font, - cairo_font_scale_t *sc, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - cairo_atsui_font_t *font = abstract_font; - cairo_fixed_t x1, y1, x2, y2; - int i; - OSStatus err; - ATSUStyle style; - - - bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; - bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - for (i = 0; i < num_glyphs; i++) - { - GlyphID theGlyph = glyphs[i].index; - ATSGlyphIdealMetrics metrics; - - - err = ATSUGlyphGetIdealMetrics( style, - 1, - &theGlyph, - 0, - &metrics); - - x1 = _cairo_fixed_from_double(glyphs[i].x); - y1 = _cairo_fixed_from_double(glyphs[i].y); - x2 = x1 + _cairo_fixed_from_double(metrics.advance.x); - y2 = y1 + _cairo_fixed_from_double(metrics.advance.y); - - if (x1 < bbox->p1.x) - bbox->p1.x = x1; - - if (y1 < bbox->p1.y) - bbox->p1.y = y1; - - if (x2 > bbox->p2.x) - bbox->p2.x = x2; - - if (y2 > bbox->p2.y) - bbox->p2.y = y2; - } - - - ATSUDisposeStyle(style); - - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_atsui_font_show_glyphs( void *abstract_font, - cairo_font_scale_t *sc, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_atsui_font_t *font = abstract_font; - CGContextRef myBitmapContext; - CGColorSpaceRef colorSpace; - cairo_image_surface_t *destImageSurface; - int i; - - - destImageSurface = _cairo_surface_get_image(surface); - - - // Create a CGBitmapContext for the dest surface for drawing into - colorSpace = CGColorSpaceCreateDeviceRGB(); - - myBitmapContext = CGBitmapContextCreate( destImageSurface->data, - destImageSurface->width, - destImageSurface->height, - destImageSurface->depth / 4, - destImageSurface->stride, - colorSpace, - kCGImageAlphaPremultipliedFirst); - - - ATSFontRef atsFont = FMGetATSFontRefFromFont(font->fontID); - CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFont); - - CGContextSetFont(myBitmapContext, cgFont); - - - CGAffineTransform textTransform = CGAffineTransformMakeWithCairoFontScale(*sc); - CGSize textSize = CGSizeMake(1.0, 1.0); - - textSize = CGSizeApplyAffineTransform(textSize, textTransform); - - CGContextSetFontSize(myBitmapContext, textSize.width); - - - // TODO - bold and italic text - // - // We could draw the text using ATSUI and get bold, italics - // etc. for free, but ATSUI does a lot of text layout work - // that we don't really need... - - - for (i = 0; i < num_glyphs; i++) - { - CGGlyph theGlyph = glyphs[i].index; - - CGContextShowGlyphsAtPoint(myBitmapContext, source_x + glyphs[i].x, destImageSurface->height - (source_y + glyphs[i].y), &theGlyph, 1); - } - - - CGColorSpaceRelease(colorSpace); - CGContextRelease(myBitmapContext); - - - return CAIRO_STATUS_SUCCESS; -} - - -#pragma mark - - - -static OSStatus MyATSCubicMoveToCallback(const Float32Point *pt, void *callBackDataPtr) -{ - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - double scaledPt[2]; - cairo_point_t point; - - - scaledPt[0] = pt->x; - scaledPt[1] = pt->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - point.x = _cairo_fixed_from_double(scaledPt[0]); - point.y = _cairo_fixed_from_double(scaledPt[1]); - - _cairo_path_move_to(info->path, &point); - - - return noErr; -} - - -static OSStatus MyATSCubicLineToCallback(const Float32Point *pt, void *callBackDataPtr) -{ - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - cairo_point_t point; - double scaledPt[2]; - - - scaledPt[0] = pt->x; - scaledPt[1] = pt->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - point.x = _cairo_fixed_from_double(scaledPt[0]); - point.y = _cairo_fixed_from_double(scaledPt[1]); - - _cairo_path_line_to(info->path, &point); - - - return noErr; -} - - -static OSStatus MyATSCubicCurveToCallback( const Float32Point *pt1, - const Float32Point *pt2, - const Float32Point *pt3, - void *callBackDataPtr) -{ - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - cairo_point_t p0, p1, p2; - double scaledPt[2]; - - - scaledPt[0] = pt1->x; - scaledPt[1] = pt1->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - p0.x = _cairo_fixed_from_double(scaledPt[0]); - p0.y = _cairo_fixed_from_double(scaledPt[1]); - - - scaledPt[0] = pt2->x; - scaledPt[1] = pt2->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - p1.x = _cairo_fixed_from_double(scaledPt[0]); - p1.y = _cairo_fixed_from_double(scaledPt[1]); - - - scaledPt[0] = pt3->x; - scaledPt[1] = pt3->y; - - cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]); - - p2.x = _cairo_fixed_from_double(scaledPt[0]); - p2.y = _cairo_fixed_from_double(scaledPt[1]); - - - _cairo_path_curve_to(info->path, &p0, &p1, &p2); - - - return noErr; -} - - -static OSStatus MyCubicClosePathProc(void * callBackDataPtr) -{ - cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr; - - - _cairo_path_close_path(info->path); - - - return noErr; -} - - -static cairo_status_t -_cairo_atsui_font_glyph_path( void *abstract_font, - cairo_font_scale_t *sc, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) -{ - int i; - cairo_atsui_font_t *font = abstract_font; - OSStatus err; - cairo_ATSUI_glyph_path_callback_info_t info; - ATSUStyle style; - - - static ATSCubicMoveToUPP moveProc = NULL; - static ATSCubicLineToUPP lineProc = NULL; - static ATSCubicCurveToUPP curveProc = NULL; - static ATSCubicClosePathUPP closePathProc = NULL; - - - if (moveProc == NULL) - { - moveProc = NewATSCubicMoveToUPP(MyATSCubicMoveToCallback); - lineProc = NewATSCubicLineToUPP(MyATSCubicLineToCallback); - curveProc = NewATSCubicCurveToUPP(MyATSCubicCurveToCallback); - closePathProc = NewATSCubicClosePathUPP(MyCubicClosePathProc); - } - - - info.path = path; - - - style = CreateSizedCopyOfStyle(font->style, sc); - - - for (i = 0; i < num_glyphs; i++) - { - GlyphID theGlyph = glyphs[i].index; - - - cairo_matrix_set_affine( &info.scale, - 1.0, 0.0, - 0.0, 1.0, - glyphs[i].x, glyphs[i].y); - - - err = ATSUGlyphGetCubicPaths( style, - theGlyph, - moveProc, - lineProc, - curveProc, - closePathProc, - (void *)&info, - &err); - } - - - err = ATSUDisposeStyle(style); - - - return CAIRO_STATUS_SUCCESS; -} - - -#pragma mark - - - -static cairo_status_t -_cairo_atsui_font_create_glyph(cairo_image_glyph_cache_entry_t *val) -{ - // TODO - printf("_cairo_atsui_font_create_glyph is unimplemented\n"); - - // I'm not sure if we need this, given that the ATSUI backend does no caching(?) - - - return CAIRO_STATUS_SUCCESS; -} - - -cairo_font_t * -cairo_atsui_font_create(ATSUStyle style) -{ - cairo_font_scale_t scale; - cairo_font_t *scaled; - cairo_atsui_font_t *f = NULL; - - - scaled = malloc(sizeof(cairo_font_t)); - if (scaled == NULL) - return NULL; - - - f = malloc(sizeof(cairo_atsui_font_t)); - if (f) - { - if (_cairo_unscaled_font_init(&f->base, &cairo_atsui_font_backend) == CAIRO_STATUS_SUCCESS) - { - f->style = style; - - _cairo_font_init(scaled, &scale, &f->base); - - return scaled; - } - } - - - free(scaled); - - - return NULL; -} - - - - - -#pragma mark Backend - - - - - -const cairo_font_backend_t cairo_atsui_font_backend = { - _cairo_atsui_font_create, - _cairo_atsui_font_destroy, - _cairo_atsui_font_font_extents, - _cairo_atsui_font_text_to_glyphs, - _cairo_atsui_font_glyph_extents, - _cairo_atsui_font_glyph_bbox, - _cairo_atsui_font_show_glyphs, - _cairo_atsui_font_glyph_path, - _cairo_atsui_font_create_glyph -}; diff --git a/src/cairo_cache.c b/src/cairo_cache.c deleted file mode 100644 index d1ad5a4e2..000000000 --- a/src/cairo_cache.c +++ /dev/null @@ -1,518 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * This file is Copyright © 2004 Red Hat, Inc. - * - * 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. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Keith Packard - * Graydon Hoare <graydon@redhat.com> - */ - -#include "cairoint.h" - -/* - * This structure, and accompanying table, is borrowed/modified from the - * file xserver/render/glyph.c in the freedesktop.org x server, with - * permission (and suggested modification of doubling sizes) by Keith - * Packard. - */ - -static const cairo_cache_arrangement_t cache_arrangements [] = { - { 16, 43, 41 }, - { 32, 73, 71 }, - { 64, 151, 149 }, - { 128, 283, 281 }, - { 256, 571, 569 }, - { 512, 1153, 1151 }, - { 1024, 2269, 2267 }, - { 2048, 4519, 4517 }, - { 4096, 9013, 9011 }, - { 8192, 18043, 18041 }, - { 16384, 36109, 36107 }, - { 32768, 72091, 72089 }, - { 65536, 144409, 144407 }, - { 131072, 288361, 288359 }, - { 262144, 576883, 576881 }, - { 524288, 1153459, 1153457 }, - { 1048576, 2307163, 2307161 }, - { 2097152, 4613893, 4613891 }, - { 4194304, 9227641, 9227639 }, - { 8388608, 18455029, 18455027 }, - { 16777216, 36911011, 36911009 }, - { 33554432, 73819861, 73819859 }, - { 67108864, 147639589, 147639587 }, - { 134217728, 295279081, 295279079 }, - { 268435456, 590559793, 590559791 } -}; - -#define N_CACHE_SIZES (sizeof(cache_arrangements)/sizeof(cache_arrangements[0])) - -/* - * Entries 'e' are poiners, in one of 3 states: - * - * e == NULL: The entry has never had anything put in it - * e != DEAD_ENTRY: The entry has an active value in it currently - * e == DEAD_ENTRY: The entry *had* a value in it at some point, but the - * entry has been killed. Lookups requesting free space can - * reuse these entries; lookups requesting a precise match - * should neither return these entries nor stop searching when - * seeing these entries. - * - * We expect keys will not be destroyed frequently, so our table does not - * contain any explicit shrinking code nor any chain-coalescing code for - * entries randomly deleted by memory pressure (except during rehashing, of - * course). These assumptions are potentially bad, but they make the - * implementation straightforward. - * - * Revisit later if evidence appears that we're using excessive memory from - * a mostly-dead table. - * - * Generally you do not need to worry about freeing cache entries; the - * cache will expire entries randomly as it experiences memory pressure. - * If max_memory is set, entries are not expired, and must be explicitely - * removed. - * - * This table is open-addressed with double hashing. Each table size is a - * prime chosen to be a little more than double the high water mark for a - * given arrangement, so the tables should remain < 50% full. The table - * size makes for the "first" hash modulus; a second prime (2 less than the - * first prime) serves as the "second" hash modulus, which is co-prime and - * thus guarantees a complete permutation of table indices. - * - */ - -#define DEAD_ENTRY ((cairo_cache_entry_base_t *) 1) -#define NULL_ENTRY_P(cache, i) ((cache)->entries[i] == NULL) -#define DEAD_ENTRY_P(cache, i) ((cache)->entries[i] == DEAD_ENTRY) -#define LIVE_ENTRY_P(cache, i) \ - (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) - -#ifdef CAIRO_DO_SANITY_CHECKING -static void -_cache_sane_state (cairo_cache_t *cache) -{ - assert (cache != NULL); - assert (cache->entries != NULL); - assert (cache->backend != NULL); - assert (cache->arrangement != NULL); - /* Cannot check this, a single object may larger */ - /* assert (cache->used_memory <= cache->max_memory); */ - assert (cache->live_entries <= cache->arrangement->size); -} -#else -#define _cache_sane_state(c) -#endif - -static void -_entry_destroy (cairo_cache_t *cache, unsigned long i) -{ - _cache_sane_state (cache); - - if (LIVE_ENTRY_P(cache, i)) - { - cairo_cache_entry_base_t *entry = cache->entries[i]; - assert(cache->live_entries > 0); - assert(cache->used_memory >= entry->memory); - - cache->live_entries--; - cache->used_memory -= entry->memory; - cache->backend->destroy_entry (cache, entry); - cache->entries[i] = DEAD_ENTRY; - } -} - -static cairo_cache_entry_base_t ** -_cache_lookup (cairo_cache_t *cache, - void *key, - int (*predicate)(void*,void*,void*)) -{ - - cairo_cache_entry_base_t **probe; - unsigned long hash; - unsigned long table_size, i, idx, step; - - _cache_sane_state (cache); - assert (key != NULL); - - table_size = cache->arrangement->size; - hash = cache->backend->hash (cache, key); - idx = hash % table_size; - step = 0; - - for (i = 0; i < table_size; ++i) - { -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->probes++; -#endif - assert(idx < table_size); - probe = cache->entries + idx; - - /* - * There are two lookup modes: searching for a free slot and searching - * for an exact entry. - */ - - if (predicate != NULL) - { - /* We are looking up an exact entry. */ - if (*probe == NULL) - /* Found an empty spot, there can't be a match */ - break; - else if (*probe != DEAD_ENTRY - && (*probe)->hashcode == hash - && predicate (cache, key, *probe)) - return probe; - } - else - { - /* We are just looking for a free slot. */ - if (*probe == NULL - || *probe == DEAD_ENTRY) - return probe; - } - - if (step == 0) { - step = hash % cache->arrangement->rehash; - if (step == 0) - step = 1; - } - - idx += step; - if (idx >= table_size) - idx -= table_size; - } - - /* - * The table should not have permitted you to get here if you were just - * looking for a free slot: there should have been room. - */ - assert(predicate != NULL); - return NULL; -} - -static cairo_cache_entry_base_t ** -_find_available_entry_for (cairo_cache_t *cache, - void *key) -{ - return _cache_lookup (cache, key, NULL); -} - -static cairo_cache_entry_base_t ** -_find_exact_live_entry_for (cairo_cache_t *cache, - void *key) -{ - return _cache_lookup (cache, key, cache->backend->keys_equal); -} - -static const cairo_cache_arrangement_t * -_find_cache_arrangement (unsigned long proposed_size) -{ - unsigned long idx; - - for (idx = 0; idx < N_CACHE_SIZES; ++idx) - if (cache_arrangements[idx].high_water_mark >= proposed_size) - return &cache_arrangements[idx]; - return NULL; -} - -static cairo_status_t -_resize_cache (cairo_cache_t *cache, unsigned long proposed_size) -{ - cairo_cache_t tmp; - cairo_cache_entry_base_t **e; - unsigned long new_size, i; - - tmp = *cache; - tmp.arrangement = _find_cache_arrangement (proposed_size); - assert(tmp.arrangement != NULL); - if (tmp.arrangement == cache->arrangement) - return CAIRO_STATUS_SUCCESS; - - new_size = tmp.arrangement->size; - tmp.entries = calloc (new_size, sizeof (cairo_cache_entry_base_t *)); - if (tmp.entries == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < cache->arrangement->size; ++i) { - if (LIVE_ENTRY_P(cache, i)) { - e = _find_available_entry_for (&tmp, cache->entries[i]); - assert (e != NULL); - *e = cache->entries[i]; - } - } - free (cache->entries); - cache->entries = tmp.entries; - cache->arrangement = tmp.arrangement; - return CAIRO_STATUS_SUCCESS; -} - - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE -static double -_load_factor (cairo_cache_t *cache) -{ - return ((double) cache->live_entries) - / ((double) cache->arrangement->size); -} -#endif - -/* Find a random in the cache matching the given predicate. We use the - * same algorithm as the probing algorithm to walk over the entries in - * the hash table in a pseudo-random order. Walking linearly would - * favor entries following gaps in the hash table. We could also - * call rand() repeatedly, which works well for almost-full tables, - * but degrades when the table is almost empty, or predicate - * returns false for most entries. - */ -static cairo_cache_entry_base_t ** -_random_entry (cairo_cache_t *cache, - int (*predicate)(void*)) -{ - cairo_cache_entry_base_t **probe; - unsigned long hash; - unsigned long table_size, i, idx, step; - - _cache_sane_state (cache); - - table_size = cache->arrangement->size; - hash = rand (); - idx = hash % table_size; - step = 0; - - for (i = 0; i < table_size; ++i) - { - assert(idx < table_size); - probe = cache->entries + idx; - - if (LIVE_ENTRY_P(cache, idx) - && (!predicate || predicate (*probe))) - return probe; - - if (step == 0) { - step = hash % cache->arrangement->rehash; - if (step == 0) - step = 1; - } - - idx += step; - if (idx >= table_size) - idx -= table_size; - } - - return NULL; -} - -/* public API follows */ - -cairo_status_t -_cairo_cache_init (cairo_cache_t *cache, - const cairo_cache_backend_t *backend, - unsigned long max_memory) -{ - assert (backend != NULL); - - if (cache != NULL){ - cache->arrangement = &cache_arrangements[0]; - cache->refcount = 1; - cache->max_memory = max_memory; - cache->used_memory = 0; - cache->live_entries = 0; - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->hits = 0; - cache->misses = 0; - cache->probes = 0; -#endif - - cache->backend = backend; - cache->entries = calloc (sizeof(cairo_cache_entry_base_t *), - cache->arrangement->size); - if (cache->entries == NULL) - return CAIRO_STATUS_NO_MEMORY; - } - _cache_sane_state (cache); - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_cache_reference (cairo_cache_t *cache) -{ - _cache_sane_state (cache); - cache->refcount++; -} - -void -_cairo_cache_destroy (cairo_cache_t *cache) -{ - unsigned long i; - if (cache != NULL) { - - _cache_sane_state (cache); - - if (--cache->refcount > 0) - return; - - for (i = 0; i < cache->arrangement->size; ++i) { - _entry_destroy (cache, i); - } - - free (cache->entries); - cache->entries = NULL; - cache->backend->destroy_cache (cache); - } -} - -cairo_status_t -_cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return, - int *created_entry) -{ - - unsigned long idx; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_cache_entry_base_t **slot = NULL, *new_entry; - - _cache_sane_state (cache); - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - if ((cache->hits + cache->misses) % 0xffff == 0) - printf("cache %p stats: size %ld, live %ld, load %.2f\n" - " mem %ld/%ld, hit %ld, miss %ld\n" - " probe %ld, %.2f probe/access\n", - cache, - cache->arrangement->size, - cache->live_entries, - _load_factor (cache), - cache->used_memory, - cache->max_memory, - cache->hits, - cache->misses, - cache->probes, - ((double) cache->probes) - / ((double) (cache->hits + - cache->misses + 1))); -#endif - - /* See if we have an entry in the table already. */ - slot = _find_exact_live_entry_for (cache, key); - if (slot != NULL) { -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->hits++; -#endif - *entry_return = *slot; - if (created_entry) - *created_entry = 0; - return status; - } - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->misses++; -#endif - - /* Build the new entry. */ - status = cache->backend->create_entry (cache, key, - (void **)&new_entry); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - /* Store the hash value in case the backend forgot. */ - new_entry->hashcode = cache->backend->hash (cache, key); - - /* Make some entries die if we're under memory pressure. */ - while (cache->live_entries > 0 && - cache->max_memory > 0 && - ((cache->max_memory - cache->used_memory) < new_entry->memory)) { - idx = _random_entry (cache, NULL) - cache->entries; - assert (idx < cache->arrangement->size); - _entry_destroy (cache, idx); - } - - /* Can't assert this; new_entry->memory may be larger than max_memory */ - /* assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); */ - - /* Make room in the table for a new slot. */ - status = _resize_cache (cache, cache->live_entries + 1); - if (status != CAIRO_STATUS_SUCCESS) { - cache->backend->destroy_entry (cache, new_entry); - return status; - } - - slot = _find_available_entry_for (cache, key); - assert(slot != NULL); - - /* Store entry in slot and increment statistics. */ - *slot = new_entry; - cache->live_entries++; - cache->used_memory += new_entry->memory; - - _cache_sane_state (cache); - - *entry_return = new_entry; - if (created_entry) - *created_entry = 1; - - return status; -} - -cairo_status_t -_cairo_cache_remove (cairo_cache_t *cache, - void *key) -{ - cairo_cache_entry_base_t **slot; - - _cache_sane_state (cache); - - /* See if we have an entry in the table already. */ - slot = _find_exact_live_entry_for (cache, key); - if (slot != NULL) - _entry_destroy (cache, slot - cache->entries); - - return CAIRO_STATUS_SUCCESS; -} - -void * -_cairo_cache_random_entry (cairo_cache_t *cache, - int (*predicate)(void*)) -{ - cairo_cache_entry_base_t **slot = _random_entry (cache, predicate); - - return slot ? *slot : NULL; -} - -unsigned long -_cairo_hash_string (const char *c) -{ - /* This is the djb2 hash. */ - unsigned long hash = 5381; - while (*c) - hash = ((hash << 5) + hash) + *c++; - return hash; -} - diff --git a/src/cairo_font.c b/src/cairo_font.c deleted file mode 100644 index 529c1c7c3..000000000 --- a/src/cairo_font.c +++ /dev/null @@ -1,476 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2002 University of Southern California - * Copyright © 2005 Red Hat Inc. - * - * 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - * Graydon Hoare <graydon@redhat.com> - * Owen Taylor <otaylor@redhat.com> - */ - -#include "cairoint.h" - -/* Now the internal "unscaled + scale" font API */ - -cairo_private cairo_status_t -_cairo_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *sc, - cairo_font_t **font) -{ - const cairo_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; - - return backend->create (family, slant, weight, sc, font); -} - -void -_cairo_font_init (cairo_font_t *font, - cairo_font_scale_t *scale, - const cairo_font_backend_t *backend) -{ - font->scale = *scale; - font->refcount = 1; - font->backend = backend; -} - -void -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const cairo_font_backend_t *backend) -{ - font->refcount = 1; - font->backend = backend; -} - -cairo_status_t -_cairo_font_text_to_glyphs (cairo_font_t *font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) -{ - return font->backend->text_to_glyphs (font, utf8, glyphs, num_glyphs); -} - -cairo_status_t -_cairo_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - return font->backend->glyph_extents(font, glyphs, num_glyphs, extents); -} - - -cairo_status_t -_cairo_font_glyph_bbox (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - return font->backend->glyph_bbox (font, glyphs, num_glyphs, bbox); -} - -cairo_status_t -_cairo_font_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_status_t status; - if (surface->backend->show_glyphs != NULL) { - status = surface->backend->show_glyphs (font, operator, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs); - if (status == CAIRO_STATUS_SUCCESS) - return status; - } - - /* Surface display routine either does not exist or failed. */ - return font->backend->show_glyphs (font, operator, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs); -} - -cairo_status_t -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) -{ - return font->backend->glyph_path (font, glyphs, num_glyphs, path); -} - -void -_cairo_font_get_glyph_cache_key (cairo_font_t *font, - cairo_glyph_cache_key_t *key) -{ - font->backend->get_glyph_cache_key (font, key); -} - -cairo_status_t -_cairo_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents) -{ - return font->backend->font_extents (font, extents); -} - -void -_cairo_unscaled_font_reference (cairo_unscaled_font_t *font) -{ - font->refcount++; -} - -void -_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font) -{ - if (--(font->refcount) > 0) - return; - - font->backend->destroy_unscaled_font (font); -} - - - -/* Public font API follows. */ - -void -cairo_font_reference (cairo_font_t *font) -{ - font->refcount++; -} - -void -cairo_font_destroy (cairo_font_t *font) -{ - if (--(font->refcount) > 0) - return; - - font->backend->destroy_font (font); -} - -/** - * cairo_font_extents: - * @font: a #cairo_font_t - * @font_matrix: the font transformation for which this font was - * created. (See cairo_transform_font()). This is needed - * properly convert the metrics from the font into user space. - * @extents: a #cairo_font_extents_t which to store the retrieved extents. - * - * Gets the metrics for a #cairo_font_t. - * - * Return value: %CAIRO_STATUS_SUCCESS on success. Otherwise, an - * error such as %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -cairo_font_extents (cairo_font_t *font, - cairo_matrix_t *font_matrix, - cairo_font_extents_t *extents) -{ - cairo_int_status_t status; - double font_scale_x, font_scale_y; - - status = _cairo_font_font_extents (font, extents); - - if (!CAIRO_OK (status)) - return status; - - _cairo_matrix_compute_scale_factors (font_matrix, - &font_scale_x, &font_scale_y, - /* XXX */ 1); - - /* - * The font responded in unscaled units, scale by the font - * matrix scale factors to get to user space - */ - - extents->ascent *= font_scale_y; - extents->descent *= font_scale_y; - extents->height *= font_scale_y; - extents->max_x_advance *= font_scale_x; - extents->max_y_advance *= font_scale_y; - - return status; -} - -/** - * cairo_font_glyph_extents: - * @font: a #cairo_font_t - * @font_matrix: the font transformation for which this font was - * created. (See cairo_transform_font()). This is needed - * properly convert the metrics from the font into user space. - * @glyphs: an array of glyph IDs with X and Y offsets. - * @num_glyphs: the number of glyphs in the @glyphs array - * @extents: a #cairo_text_extents_t which to store the retrieved extents. - * - * cairo_font_glyph_extents() gets the overall metrics for a string of - * glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0. - **/ -void -cairo_font_glyph_extents (cairo_font_t *font, - cairo_matrix_t *font_matrix, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_glyph_t origin_glyph; - cairo_text_extents_t origin_extents; - int i; - double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; - double x_pos = 0.0, y_pos = 0.0; - int set = 0; - - if (!num_glyphs) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return; - } - - for (i = 0; i < num_glyphs; i++) - { - double x, y; - double wm, hm; - - origin_glyph = glyphs[i]; - origin_glyph.x = 0.0; - origin_glyph.y = 0.0; - status = _cairo_font_glyph_extents (font, - &origin_glyph, 1, - &origin_extents); - - /* - * Transform font space metrics into user space metrics - * by running the corners through the font matrix and - * expanding the bounding box as necessary - */ - x = origin_extents.x_bearing; - y = origin_extents.y_bearing; - cairo_matrix_transform_point (font_matrix, - &x, &y); - - for (hm = 0.0; hm <= 1.0; hm += 1.0) - for (wm = 0.0; wm <= 1.0; wm += 1.0) - { - x = origin_extents.x_bearing + origin_extents.width * wm; - y = origin_extents.y_bearing + origin_extents.height * hm; - cairo_matrix_transform_point (font_matrix, - &x, &y); - x += glyphs[i].x; - y += glyphs[i].y; - if (!set) - { - min_x = max_x = x; - min_y = max_y = y; - set = 1; - } - else - { - if (x < min_x) min_x = x; - if (x > max_x) max_x = x; - if (y < min_y) min_y = y; - if (y > max_y) max_y = y; - } - } - - x = origin_extents.x_advance; - y = origin_extents.y_advance; - cairo_matrix_transform_point (font_matrix, - &x, &y); - x_pos = glyphs[i].x + x; - y_pos = glyphs[i].y + y; - } - - extents->x_bearing = min_x - glyphs[0].x; - extents->y_bearing = min_y - glyphs[0].y; - extents->width = max_x - min_x; - extents->height = max_y - min_y; - extents->x_advance = x_pos - glyphs[0].x; - extents->y_advance = y_pos - glyphs[0].y; -} - -/* Now we implement functions to access a default global image & metrics - * cache. - */ - -unsigned long -_cairo_glyph_cache_hash (void *cache, void *key) -{ - cairo_glyph_cache_key_t *in; - in = (cairo_glyph_cache_key_t *) key; - return - ((unsigned long) in->unscaled) - ^ ((unsigned long) in->scale.matrix[0][0]) - ^ ((unsigned long) in->scale.matrix[0][1]) - ^ ((unsigned long) in->scale.matrix[1][0]) - ^ ((unsigned long) in->scale.matrix[1][1]) - ^ (in->flags * 1451) /* 1451 is just an abitrary prime */ - ^ in->index; -} - -int -_cairo_glyph_cache_keys_equal (void *cache, - void *k1, - void *k2) -{ - cairo_glyph_cache_key_t *a, *b; - a = (cairo_glyph_cache_key_t *) k1; - b = (cairo_glyph_cache_key_t *) k2; - return (a->index == b->index) - && (a->unscaled == b->unscaled) - && (a->flags == b->flags) - && (a->scale.matrix[0][0] == b->scale.matrix[0][0]) - && (a->scale.matrix[0][1] == b->scale.matrix[0][1]) - && (a->scale.matrix[1][0] == b->scale.matrix[1][0]) - && (a->scale.matrix[1][1] == b->scale.matrix[1][1]); -} - - -static cairo_status_t -_image_glyph_cache_create_entry (void *cache, - void *key, - void **return_value) -{ - cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key; - cairo_image_glyph_cache_entry_t *im; - cairo_status_t status; - - im = calloc (1, sizeof (cairo_image_glyph_cache_entry_t)); - if (im == NULL) - return CAIRO_STATUS_NO_MEMORY; - - im->key = *k; - status = im->key.unscaled->backend->create_glyph (im); - - if (status != CAIRO_STATUS_SUCCESS) { - free (im); - return status; - } - - _cairo_unscaled_font_reference (im->key.unscaled); - - im->key.base.memory = - sizeof (cairo_image_glyph_cache_entry_t) - + (im->image ? - sizeof (cairo_image_surface_t) - + 28 * sizeof (int) /* rough guess at size of pixman image structure */ - + (im->image->height * im->image->stride) : 0); - - *return_value = im; - - return CAIRO_STATUS_SUCCESS; -} - - -static void -_image_glyph_cache_destroy_entry (void *cache, - void *value) -{ - cairo_image_glyph_cache_entry_t *im; - - im = (cairo_image_glyph_cache_entry_t *) value; - _cairo_unscaled_font_destroy (im->key.unscaled); - cairo_surface_destroy (&(im->image->base)); - free (im); -} - -static void -_image_glyph_cache_destroy_cache (void *cache) -{ - free (cache); -} - -static const cairo_cache_backend_t cairo_image_cache_backend = { - _cairo_glyph_cache_hash, - _cairo_glyph_cache_keys_equal, - _image_glyph_cache_create_entry, - _image_glyph_cache_destroy_entry, - _image_glyph_cache_destroy_cache -}; - -void -_cairo_lock_global_image_glyph_cache() -{ - /* FIXME: implement locking. */ -} - -void -_cairo_unlock_global_image_glyph_cache() -{ - /* FIXME: implement locking. */ -} - -static cairo_cache_t * -_global_image_glyph_cache = NULL; - -cairo_cache_t * -_cairo_get_global_image_glyph_cache () -{ - if (_global_image_glyph_cache == NULL) { - _global_image_glyph_cache = malloc (sizeof (cairo_cache_t)); - - if (_global_image_glyph_cache == NULL) - goto FAIL; - - if (_cairo_cache_init (_global_image_glyph_cache, - &cairo_image_cache_backend, - CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT)) - goto FAIL; - } - - return _global_image_glyph_cache; - - FAIL: - if (_global_image_glyph_cache) - free (_global_image_glyph_cache); - _global_image_glyph_cache = NULL; - return NULL; -} diff --git a/src/cairo_ft_font.c b/src/cairo_ft_font.c deleted file mode 100644 index 44e1b0e84..000000000 --- a/src/cairo_ft_font.c +++ /dev/null @@ -1,1538 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * - * 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. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Graydon Hoare <graydon@redhat.com> - * Owen Taylor <otaylor@redhat.com> - */ - -#include "cairo-ft-private.h" - -#include <fontconfig/fontconfig.h> -#include <fontconfig/fcfreetype.h> - -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_IMAGE_H - -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) -#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) -#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) -#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) - -/* This is the max number of FT_face objects we keep open at once - */ -#define MAX_OPEN_FACES 10 - -/* - * The simple 2x2 matrix is converted into separate scale and shape - * factors so that hinting works right - */ - -typedef struct { - double x_scale, y_scale; - double shape[2][2]; -} ft_font_transform_t; - -/* - * We create an object that corresponds to a single font on the disk; - * (identified by a filename/id pair) these are shared between all - * fonts using that file. For cairo_ft_font_create_for_ft_face(), we - * just create a one-off version with a permanent face value. - */ - -typedef struct { - cairo_unscaled_font_t base; - - int from_face; /* from cairo_ft_font_create_for_ft_face()? */ - FT_Face face; /* provided or cached face */ - - /* only set if from_face is false */ - FT_Library library; - char *filename; - int id; - - /* We temporarily scale the unscaled font as neede */ - int have_scale; - cairo_font_scale_t current_scale; - double x_scale; /* Extracted X scale factor */ - double y_scale; /* Extracted Y scale factor */ - - int lock; /* count of how many times this font has been locked */ -} ft_unscaled_font_t; - -const cairo_font_backend_t cairo_ft_font_backend; - -static ft_unscaled_font_t * -_ft_unscaled_font_create_from_face (FT_Face face) -{ - ft_unscaled_font_t *unscaled = malloc (sizeof(ft_unscaled_font_t)); - if (!unscaled) - return NULL; - - unscaled->from_face = 1; - unscaled->face = face; - - unscaled->library = NULL; - unscaled->filename = NULL; - unscaled->id = 0; - - unscaled->have_scale = 0; - unscaled->lock = 0; - - _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, - &cairo_ft_font_backend); - return unscaled; -} - -static ft_unscaled_font_t * -_ft_unscaled_font_create_from_filename (FT_Library library, - const char *filename, - int id) -{ - ft_unscaled_font_t *unscaled; - char *new_filename; - - new_filename = strdup (filename); - if (!new_filename) - return NULL; - - unscaled = malloc (sizeof (ft_unscaled_font_t)); - if (!unscaled) { - free (new_filename); - return NULL; - } - - unscaled->from_face = 0; - unscaled->face = NULL; - - unscaled->library = library; - unscaled->filename = new_filename; - unscaled->id = id; - - unscaled->have_scale = 0; - unscaled->lock = 0; - - _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, - &cairo_ft_font_backend); - return unscaled; -} - -/* - * We keep a global cache from [file/id] => [ft_unscaled_font_t]. This - * hash isn't limited in size. However, we limit the number of - * FT_Face objects we keep around; when we've exceeeded that - * limit and need to create a new FT_Face, we dump the FT_Face from - * a random ft_unscaled_font_t. - */ - -typedef struct { - cairo_cache_entry_base_t base; - char *filename; - int id; -} cairo_ft_cache_key_t; - -typedef struct { - cairo_ft_cache_key_t key; - ft_unscaled_font_t *unscaled; -} cairo_ft_cache_entry_t; - -typedef struct { - cairo_cache_t base; - FT_Library lib; - int n_faces; /* Number of open FT_Face objects */ -} ft_cache_t; - -static unsigned long -_ft_font_cache_hash (void *cache, void *key) -{ - cairo_ft_cache_key_t *in = (cairo_ft_cache_key_t *) key; - unsigned long hash; - - /* 1607 is just a random prime. */ - hash = _cairo_hash_string (in->filename); - hash += ((unsigned long) in->id) * 1607; - - return hash; -} - -static int -_ft_font_cache_keys_equal (void *cache, - void *k1, - void *k2) -{ - cairo_ft_cache_key_t *a; - cairo_ft_cache_key_t *b; - a = (cairo_ft_cache_key_t *) k1; - b = (cairo_ft_cache_key_t *) k2; - - return strcmp (a->filename, b->filename) == 0 && - a->id == b->id; -} - -static cairo_status_t -_ft_font_cache_create_entry (void *cache, - void *key, - void **return_entry) -{ - ft_cache_t *ftcache = (ft_cache_t *) cache; - cairo_ft_cache_key_t *k = (cairo_ft_cache_key_t *) key; - cairo_ft_cache_entry_t *entry; - - entry = malloc (sizeof (cairo_ft_cache_entry_t)); - if (entry == NULL) - return CAIRO_STATUS_NO_MEMORY; - - entry->unscaled = _ft_unscaled_font_create_from_filename (ftcache->lib, - k->filename, - k->id); - if (!entry->unscaled) { - free (entry); - return CAIRO_STATUS_NO_MEMORY; - } - - entry->key.base.memory = 0; - entry->key.filename = entry->unscaled->filename; - entry->key.id = entry->unscaled->id; - - *return_entry = entry; - - return CAIRO_STATUS_SUCCESS; -} - -/* Entries are never spontaneously destroyed; but only when - * we remove them from the cache specifically. We free entry->unscaled - * in the code that removes the entry from the cache - */ -static void -_ft_font_cache_destroy_entry (void *cache, - void *entry) -{ - cairo_ft_cache_entry_t *e = (cairo_ft_cache_entry_t *) entry; - - free (e); -} - -static void -_ft_font_cache_destroy_cache (void *cache) -{ - ft_cache_t *fc = (ft_cache_t *) cache; - FT_Done_FreeType (fc->lib); - free (fc); -} - -static const cairo_cache_backend_t _ft_font_cache_backend = { - _ft_font_cache_hash, - _ft_font_cache_keys_equal, - _ft_font_cache_create_entry, - _ft_font_cache_destroy_entry, - _ft_font_cache_destroy_cache -}; - -static ft_cache_t *_global_ft_cache = NULL; - -static void -_lock_global_ft_cache (void) -{ - /* FIXME: Perform locking here. */ -} - -static void -_unlock_global_ft_cache (void) -{ - /* FIXME: Perform locking here. */ -} - -static cairo_cache_t * -_get_global_ft_cache (void) -{ - if (_global_ft_cache == NULL) - { - _global_ft_cache = malloc (sizeof(ft_cache_t)); - if (!_global_ft_cache) - goto FAIL; - - if (_cairo_cache_init (&_global_ft_cache->base, - &_ft_font_cache_backend, - 0)) /* No memory limit */ - goto FAIL; - - if (FT_Init_FreeType (&_global_ft_cache->lib)) - goto FAIL; - _global_ft_cache->n_faces = 0; - } - return &_global_ft_cache->base; - - FAIL: - if (_global_ft_cache) - free (_global_ft_cache); - _global_ft_cache = NULL; - return NULL; -} - -/* Finds or creates a ft_unscaled_font for the filename/id from pattern. - * Returns a new reference to the unscaled font. - */ -static ft_unscaled_font_t * -_ft_unscaled_font_get_for_pattern (FcPattern *pattern) -{ - cairo_ft_cache_entry_t *entry; - cairo_ft_cache_key_t key; - cairo_cache_t *cache; - cairo_status_t status; - FcChar8 *filename; - int created_entry; - - if (FcPatternGetString (pattern, FC_FILE, 0, &filename) != FcResultMatch) - return NULL; - key.filename = (char *)filename; - - if (FcPatternGetInteger (pattern, FC_INDEX, 0, &key.id) != FcResultMatch) - return NULL; - - _lock_global_ft_cache (); - cache = _get_global_ft_cache (); - if (cache == NULL) { - _unlock_global_ft_cache (); - return NULL; - } - - status = _cairo_cache_lookup (cache, &key, (void **) &entry, &created_entry); - _unlock_global_ft_cache (); - if (status) - return NULL; - - if (!created_entry) - _cairo_unscaled_font_reference ((cairo_unscaled_font_t *)entry->unscaled); - - return entry->unscaled; -} - -static int -_has_unlocked_face (void *entry) -{ - cairo_ft_cache_entry_t *e = entry; - - return (e->unscaled->lock == 0 && e->unscaled->face); -} - -/* Ensures that an unscaled font has a face object. If we exceed - * MAX_OPEN_FACES, try to close some. - */ -static FT_Face -_ft_unscaled_font_lock_face (ft_unscaled_font_t *unscaled) -{ - ft_cache_t *ftcache; - FT_Face face = NULL; - - if (unscaled->face) { - unscaled->lock++; - return unscaled->face; - } - - assert (!unscaled->from_face); - - _lock_global_ft_cache (); - ftcache = (ft_cache_t *) _get_global_ft_cache (); - assert (ftcache != NULL); - - while (ftcache->n_faces >= MAX_OPEN_FACES) { - cairo_ft_cache_entry_t *entry; - - entry = _cairo_cache_random_entry ((cairo_cache_t *)ftcache, _has_unlocked_face); - if (entry) { - FT_Done_Face (entry->unscaled->face); - entry->unscaled->face = NULL; - entry->unscaled->have_scale = 0; - ftcache->n_faces--; - } else { - break; - } - } - - if (FT_New_Face (ftcache->lib, - unscaled->filename, - unscaled->id, - &face) != FT_Err_Ok) - goto FAIL; - - unscaled->face = face; - unscaled->lock++; - ftcache->n_faces++; - - FAIL: - _unlock_global_ft_cache (); - return face; -} - -/* Unlock unscaled font locked with _ft_unscaled_font_lock_face - */ -static void -_ft_unscaled_font_unlock_face (ft_unscaled_font_t *unscaled) -{ - assert (unscaled->lock > 0); - - unscaled->lock--; -} - -static void -_compute_transform (ft_font_transform_t *sf, - cairo_font_scale_t *sc) -{ - cairo_matrix_t normalized; - double tx, ty; - - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. These influence the way freetype - * chooses hints, as well as selecting different bitmaps in - * hand-rendered fonts. We also copy the normalized matrix to - * freetype's transformation. - */ - - cairo_matrix_set_affine (&normalized, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - _cairo_matrix_compute_scale_factors (&normalized, - &sf->x_scale, &sf->y_scale, - /* XXX */ 1); - cairo_matrix_scale (&normalized, 1.0 / sf->x_scale, 1.0 / sf->y_scale); - cairo_matrix_get_affine (&normalized, - &sf->shape[0][0], &sf->shape[0][1], - &sf->shape[1][0], &sf->shape[1][1], - &tx, &ty); -} - -/* Temporarily scales an unscaled font to the give scale. We catch - * scaling to the same size, since changing a FT_Face is expensive. - */ -static void -_ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, - cairo_font_scale_t *scale) -{ - ft_font_transform_t sf; - FT_Matrix mat; - - assert (unscaled->face != NULL); - - if (unscaled->have_scale && - scale->matrix[0][0] == unscaled->current_scale.matrix[0][0] && - scale->matrix[0][1] == unscaled->current_scale.matrix[0][1] && - scale->matrix[1][0] == unscaled->current_scale.matrix[1][0] && - scale->matrix[1][1] == unscaled->current_scale.matrix[1][1]) - return; - - unscaled->have_scale = 1; - unscaled->current_scale = *scale; - - _compute_transform (&sf, scale); - - unscaled->x_scale = sf.x_scale; - unscaled->y_scale = sf.y_scale; - - mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); - mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); - mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); - mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); - - FT_Set_Transform(unscaled->face, &mat, NULL); - - FT_Set_Pixel_Sizes(unscaled->face, - (FT_UInt) sf.x_scale, - (FT_UInt) sf.y_scale); -} - -/* implement the font backend interface */ - -typedef struct { - cairo_font_t base; - FcPattern *pattern; - int load_flags; - ft_unscaled_font_t *unscaled; -} cairo_ft_font_t; - -/* for compatibility with older freetype versions */ -#ifndef FT_LOAD_TARGET_MONO -#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME -#endif - -/* The load flags passed to FT_Load_Glyph control aspects like hinting and - * antialiasing. Here we compute them from the fields of a FcPattern. - */ -static int -_get_load_flags (FcPattern *pattern) -{ - FcBool antialias, hinting, autohint; -#ifdef FC_HINT_STYLE - int hintstyle; -#endif - int load_flags = 0; - - /* disable antialiasing if requested */ - if (FcPatternGetBool (pattern, - FC_ANTIALIAS, 0, &antialias) != FcResultMatch) - antialias = FcTrue; - - if (antialias) - load_flags |= FT_LOAD_NO_BITMAP; - else - load_flags |= FT_LOAD_TARGET_MONO; - - /* disable hinting if requested */ - if (FcPatternGetBool (pattern, - FC_HINTING, 0, &hinting) != FcResultMatch) - hinting = FcTrue; - -#ifdef FC_HINT_STYLE - if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) - hintstyle = FC_HINT_FULL; - - if (!hinting || hintstyle == FC_HINT_NONE) - load_flags |= FT_LOAD_NO_HINTING; - - switch (hintstyle) { - case FC_HINT_SLIGHT: - case FC_HINT_MEDIUM: - load_flags |= FT_LOAD_TARGET_LIGHT; - break; - default: - load_flags |= FT_LOAD_TARGET_NORMAL; - break; - } -#else /* !FC_HINT_STYLE */ - if (!hinting) - load_flags |= FT_LOAD_NO_HINTING; -#endif /* FC_FHINT_STYLE */ - - /* force autohinting if requested */ - if (FcPatternGetBool (pattern, - FC_AUTOHINT, 0, &autohint) != FcResultMatch) - autohint = FcFalse; - - if (autohint) - load_flags |= FT_LOAD_FORCE_AUTOHINT; - - return load_flags; -} - -/* Like the public cairo_ft_font_create, but takes a cairo_font_scale_t, - * rather than a cairo_font_t - */ -static cairo_font_t * -_ft_font_create (FcPattern *pattern, - cairo_font_scale_t *scale) -{ - cairo_ft_font_t *f = NULL; - ft_unscaled_font_t *unscaled = NULL; - - unscaled = _ft_unscaled_font_get_for_pattern (pattern); - if (unscaled == NULL) - return NULL; - - f = malloc (sizeof(cairo_ft_font_t)); - if (f == NULL) - goto FREE_UNSCALED; - - f->unscaled = unscaled; - f->pattern = pattern; - FcPatternReference (pattern); - f->load_flags = _get_load_flags (pattern); - - _cairo_font_init ((cairo_font_t *)f, scale, &cairo_ft_font_backend); - - return (cairo_font_t *)f; - - FREE_UNSCALED: - _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); - - return NULL; -} - -static cairo_status_t -_cairo_ft_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *scale, - cairo_font_t **font) -{ - FcPattern *pattern, *resolved; - cairo_font_t *new_font; - FcResult result; - int fcslant; - int fcweight; - ft_font_transform_t sf; - - pattern = FcPatternCreate (); - if (!pattern) - return CAIRO_STATUS_NO_MEMORY; - - switch (weight) - { - case CAIRO_FONT_WEIGHT_BOLD: - fcweight = FC_WEIGHT_BOLD; - break; - case CAIRO_FONT_WEIGHT_NORMAL: - default: - fcweight = FC_WEIGHT_MEDIUM; - break; - } - - switch (slant) - { - case CAIRO_FONT_SLANT_ITALIC: - fcslant = FC_SLANT_ITALIC; - break; - case CAIRO_FONT_SLANT_OBLIQUE: - fcslant = FC_SLANT_OBLIQUE; - break; - case CAIRO_FONT_SLANT_NORMAL: - default: - fcslant = FC_SLANT_ROMAN; - break; - } - - if (!FcPatternAddString (pattern, FC_FAMILY, family)) - goto FREE_PATTERN; - if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) - goto FREE_PATTERN; - if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) - goto FREE_PATTERN; - - _compute_transform (&sf, scale); - - FcPatternAddInteger (pattern, FC_PIXEL_SIZE, sf.y_scale); - - FcConfigSubstitute (NULL, pattern, FcMatchPattern); - FcDefaultSubstitute (pattern); - - resolved = FcFontMatch (NULL, pattern, &result); - if (!resolved) - goto FREE_PATTERN; - - new_font = _ft_font_create (resolved, scale); - - FcPatternDestroy (resolved); - FcPatternDestroy (pattern); - - if (new_font) { - *font = new_font; - return CAIRO_STATUS_SUCCESS; - } else { - return CAIRO_STATUS_NO_MEMORY; /* A guess */ - } - - FREE_PATTERN: - FcPatternDestroy (pattern); - - return CAIRO_STATUS_NO_MEMORY; -} - -static void -_cairo_ft_font_destroy_font (void *abstract_font) -{ - cairo_ft_font_t * font = abstract_font; - - if (font == NULL) - return; - - if (font->pattern != NULL) - FcPatternDestroy (font->pattern); - - _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)font->unscaled); - - free (font); -} - -static void -_cairo_ft_font_destroy_unscaled_font (void *abstract_font) -{ - ft_unscaled_font_t *unscaled = abstract_font; - - if (!unscaled->from_face) { - cairo_cache_t *cache; - cairo_ft_cache_key_t key; - - _lock_global_ft_cache (); - cache = _get_global_ft_cache (); - assert (cache); - - key.filename = unscaled->filename; - key.id = unscaled->id; - - _cairo_cache_remove (cache, &key); - - _unlock_global_ft_cache (); - } - - if (unscaled == NULL) - return; - - if (!unscaled->from_face && unscaled->face) - FT_Done_Face (unscaled->face); - - if (unscaled->filename) - free (unscaled->filename); - - free (unscaled); -} - -static void -_cairo_ft_font_get_glyph_cache_key (void *abstract_font, - cairo_glyph_cache_key_t *key) -{ - cairo_ft_font_t *font = abstract_font; - - key->unscaled = (cairo_unscaled_font_t *)font->unscaled; - key->scale = font->base.scale; - key->flags = font->load_flags; -} - -static cairo_status_t -_cairo_ft_font_text_to_glyphs (void *abstract_font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *nglyphs) -{ - double x = 0., y = 0.; - size_t i; - uint32_t *ucs4 = NULL; - cairo_ft_font_t *font = abstract_font; - FT_Face face; - cairo_glyph_cache_key_t key; - cairo_image_glyph_cache_entry_t *val; - cairo_cache_t *cache = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - _cairo_ft_font_get_glyph_cache_key (font, &key); - - status = _cairo_utf8_to_ucs4 (utf8, -1, &ucs4, nglyphs); - if (!CAIRO_OK (status)) - return status; - - face = cairo_ft_font_lock_face ((cairo_font_t *)font); - if (!face) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL1; - } - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache (); - if (cache == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - - *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); - if (*glyphs == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - - for (i = 0; i < *nglyphs; i++) - { - (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); - (*glyphs)[i].x = x; - (*glyphs)[i].y = y; - - val = NULL; - key.index = (*glyphs)[i].index; - - if (_cairo_cache_lookup (cache, &key, (void **) &val, NULL) - != CAIRO_STATUS_SUCCESS || val == NULL) - continue; - - x += val->extents.x_advance; - y += val->extents.y_advance; - } - - FAIL2: - if (cache) - _cairo_unlock_global_image_glyph_cache (); - - cairo_ft_font_unlock_face ((cairo_font_t *)font); - - FAIL1: - free (ucs4); - - return status; -} - - -static cairo_status_t -_cairo_ft_font_font_extents (void *abstract_font, - cairo_font_extents_t *extents) -{ - cairo_ft_font_t *font = abstract_font; - FT_Face face; - FT_Size_Metrics *metrics; - - face = _ft_unscaled_font_lock_face (font->unscaled); - if (!face) - return CAIRO_STATUS_NO_MEMORY; - - metrics = &face->size->metrics; - - _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); - - /* - * Get to unscaled metrics so that the upper level can get back to - * user space - */ - extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / font->unscaled->y_scale; - extents->descent = DOUBLE_FROM_26_6(metrics->descender) / font->unscaled->y_scale; - extents->height = DOUBLE_FROM_26_6(metrics->height) / font->unscaled->y_scale; - extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / font->unscaled->x_scale; - - /* FIXME: this doesn't do vertical layout atm. */ - extents->max_y_advance = 0.0; - - _ft_unscaled_font_unlock_face (font->unscaled); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ft_font_glyph_extents (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - int i; - cairo_ft_font_t *font = abstract_font; - cairo_point_double_t origin; - cairo_point_double_t glyph_min, glyph_max; - cairo_point_double_t total_min, total_max; - - cairo_image_glyph_cache_entry_t *img = NULL; - cairo_cache_t *cache; - cairo_glyph_cache_key_t key; - - if (num_glyphs == 0) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return CAIRO_STATUS_SUCCESS; - } - - origin.x = glyphs[0].x; - origin.y = glyphs[0].y; - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache (); - if (cache == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - _cairo_ft_font_get_glyph_cache_key (font, &key); - - for (i = 0; i < num_glyphs; i++) - { - img = NULL; - key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) - != CAIRO_STATUS_SUCCESS || img == NULL) - continue; - - /* XXX: Need to add code here to check the font's FcPattern - for FC_VERTICAL_LAYOUT and if set get vertBearingX/Y - instead. This will require that - cairo_ft_font_create_for_ft_face accept an - FcPattern. */ - glyph_min.x = glyphs[i].x + img->extents.x_bearing; - glyph_min.y = glyphs[i].y + img->extents.y_bearing; - glyph_max.x = glyph_min.x + img->extents.width; - glyph_max.y = glyph_min.y + img->extents.height; - - if (i==0) { - total_min = glyph_min; - total_max = glyph_max; - } else { - if (glyph_min.x < total_min.x) - total_min.x = glyph_min.x; - if (glyph_min.y < total_min.y) - total_min.y = glyph_min.y; - - if (glyph_max.x > total_max.x) - total_max.x = glyph_max.x; - if (glyph_max.y > total_max.y) - total_max.y = glyph_max.y; - } - } - _cairo_unlock_global_image_glyph_cache (); - - extents->x_bearing = (total_min.x - origin.x); - extents->y_bearing = (total_min.y - origin.y); - extents->width = (total_max.x - total_min.x); - extents->height = (total_max.y - total_min.y); - extents->x_advance = glyphs[i-1].x + (img == NULL ? 0 : img->extents.x_advance) - origin.x; - extents->y_advance = glyphs[i-1].y + (img == NULL ? 0 : img->extents.y_advance) - origin.y; - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_ft_font_glyph_bbox (void *abstract_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - cairo_image_glyph_cache_entry_t *img; - cairo_cache_t *cache; - cairo_glyph_cache_key_t key; - cairo_ft_font_t *font = abstract_font; - - cairo_fixed_t x1, y1, x2, y2; - int i; - - bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; - bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache(); - - if (cache == NULL - || font == NULL - || glyphs == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - _cairo_ft_font_get_glyph_cache_key (font, &key); - - for (i = 0; i < num_glyphs; i++) - { - - img = NULL; - key.index = glyphs[i].index; - - if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) - != CAIRO_STATUS_SUCCESS || img == NULL) - continue; - - x1 = _cairo_fixed_from_double (glyphs[i].x + img->size.x); - y1 = _cairo_fixed_from_double (glyphs[i].y + img->size.y); - x2 = x1 + _cairo_fixed_from_double (img->size.width); - y2 = y1 + _cairo_fixed_from_double (img->size.height); - - if (x1 < bbox->p1.x) - bbox->p1.x = x1; - - if (y1 < bbox->p1.y) - bbox->p1.y = y1; - - if (x2 > bbox->p2.x) - bbox->p2.x = x2; - - if (y2 > bbox->p2.y) - bbox->p2.y = y2; - } - _cairo_unlock_global_image_glyph_cache (); - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_ft_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_image_glyph_cache_entry_t *img; - cairo_cache_t *cache; - cairo_glyph_cache_key_t key; - cairo_ft_font_t *font = abstract_font; - cairo_surface_pattern_t glyph_pattern; - cairo_status_t status; - int x, y; - int i; - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache(); - - if (cache == NULL - || font == NULL - || pattern == NULL - || surface == NULL - || glyphs == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - key.unscaled = (cairo_unscaled_font_t *)font->unscaled; - key.scale = font->base.scale; - key.flags = font->load_flags; - - for (i = 0; i < num_glyphs; i++) - { - img = NULL; - key.index = glyphs[i].index; - - if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) - != CAIRO_STATUS_SUCCESS - || img == NULL - || img->image == NULL) - continue; - - x = (int) floor (glyphs[i].x + 0.5); - y = (int) floor (glyphs[i].y + 0.5); - - _cairo_pattern_init_for_surface (&glyph_pattern, &(img->image->base)); - - status = _cairo_surface_composite (operator, pattern, - &glyph_pattern.base, - surface, - x + img->size.x, - y + img->size.y, - 0, 0, - x + img->size.x, - y + img->size.y, - (double) img->size.width, - (double) img->size.height); - - _cairo_pattern_fini (&glyph_pattern.base); - - if (status) { - _cairo_unlock_global_image_glyph_cache (); - return status; - } - } - - _cairo_unlock_global_image_glyph_cache (); - - return CAIRO_STATUS_SUCCESS; -} - - -static int -_move_to (FT_Vector *to, void *closure) -{ - cairo_path_t *path = closure; - cairo_point_t point; - - point.x = _cairo_fixed_from_26_6 (to->x); - point.y = _cairo_fixed_from_26_6 (to->y); - - _cairo_path_close_path (path); - _cairo_path_move_to (path, &point); - - return 0; -} - -static int -_line_to (FT_Vector *to, void *closure) -{ - cairo_path_t *path = closure; - cairo_point_t point; - - point.x = _cairo_fixed_from_26_6 (to->x); - point.y = _cairo_fixed_from_26_6 (to->y); - - _cairo_path_line_to (path, &point); - - return 0; -} - -static int -_conic_to (FT_Vector *control, FT_Vector *to, void *closure) -{ - cairo_path_t *path = closure; - - cairo_point_t p0, p1, p2, p3; - cairo_point_t conic; - - _cairo_path_current_point (path, &p0); - - conic.x = _cairo_fixed_from_26_6 (control->x); - conic.y = _cairo_fixed_from_26_6 (control->y); - - p3.x = _cairo_fixed_from_26_6 (to->x); - p3.y = _cairo_fixed_from_26_6 (to->y); - - p1.x = p0.x + 2.0/3.0 * (conic.x - p0.x); - p1.y = p0.y + 2.0/3.0 * (conic.y - p0.y); - - p2.x = p3.x + 2.0/3.0 * (conic.x - p3.x); - p2.y = p3.y + 2.0/3.0 * (conic.y - p3.y); - - _cairo_path_curve_to (path, - &p1, &p2, &p3); - - return 0; -} - -static int -_cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closure) -{ - cairo_path_t *path = closure; - cairo_point_t p0, p1, p2; - - p0.x = _cairo_fixed_from_26_6 (control1->x); - p0.y = _cairo_fixed_from_26_6 (control1->y); - - p1.x = _cairo_fixed_from_26_6 (control2->x); - p1.y = _cairo_fixed_from_26_6 (control2->y); - - p2.x = _cairo_fixed_from_26_6 (to->x); - p2.y = _cairo_fixed_from_26_6 (to->y); - - _cairo_path_curve_to (path, &p0, &p1, &p2); - - return 0; -} - -static cairo_status_t -_cairo_ft_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) -{ - int i; - cairo_ft_font_t *font = abstract_font; - FT_GlyphSlot glyph; - FT_Face face; - FT_Error error; - FT_Outline_Funcs outline_funcs = { - _move_to, - _line_to, - _conic_to, - _cubic_to, - 0, /* shift */ - 0, /* delta */ - }; - - face = cairo_ft_font_lock_face (abstract_font); - if (!face) - return CAIRO_STATUS_NO_MEMORY; - - glyph = face->glyph; - - for (i = 0; i < num_glyphs; i++) - { - FT_Matrix invert_y = { - DOUBLE_TO_16_16 (1.0), 0, - 0, DOUBLE_TO_16_16 (-1.0), - }; - - error = FT_Load_Glyph (font->unscaled->face, glyphs[i].index, font->load_flags | FT_LOAD_NO_BITMAP); - /* XXX: What to do in this error case? */ - if (error) - continue; - /* XXX: Do we want to support bitmap fonts here? */ - if (glyph->format == ft_glyph_format_bitmap) - continue; - - /* Font glyphs have an inverted Y axis compared to cairo. */ - FT_Outline_Transform (&glyph->outline, &invert_y); - FT_Outline_Translate (&glyph->outline, - DOUBLE_TO_26_6(glyphs[i].x), - DOUBLE_TO_26_6(glyphs[i].y)); - FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); - } - _cairo_path_close_path (path); - - cairo_ft_font_unlock_face (abstract_font); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ft_font_create_glyph (cairo_image_glyph_cache_entry_t *val) -{ - ft_unscaled_font_t *unscaled = (ft_unscaled_font_t *)val->key.unscaled; - FT_GlyphSlot glyphslot; - unsigned int width, height, stride; - FT_Face face; - FT_Outline *outline; - FT_BBox cbox; - FT_Bitmap bitmap; - FT_Glyph_Metrics *metrics; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - glyphslot = unscaled->face->glyph; - metrics = &glyphslot->metrics; - - face = _ft_unscaled_font_lock_face (unscaled); - if (!face) - return CAIRO_STATUS_NO_MEMORY; - - _ft_unscaled_font_set_scale (unscaled, &val->key.scale); - - if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinates of the bearing and advance need to be negated. - * - * Scale metrics back to glyph space from the scaled glyph space returned - * by FreeType - */ - - val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; - - val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; - val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; - - /* - * use untransformed advance values - * XXX uses horizontal advance only at present; - should provide FT_LOAD_VERTICAL_LAYOUT - */ - - val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; - val->extents.y_advance = 0 / unscaled->y_scale; - - outline = &glyphslot->outline; - - FT_Outline_Get_CBox (outline, &cbox); - - cbox.xMin &= -64; - cbox.yMin &= -64; - cbox.xMax = (cbox.xMax + 63) & -64; - cbox.yMax = (cbox.yMax + 63) & -64; - - width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); - height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); - stride = (width + 3) & -4; - - if (width * height == 0) - val->image = NULL; - else - { - - bitmap.pixel_mode = ft_pixel_mode_grays; - bitmap.num_grays = 256; - bitmap.width = width; - bitmap.rows = height; - bitmap.pitch = stride; - bitmap.buffer = calloc (1, stride * height); - - if (bitmap.buffer == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); - - if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { - free (bitmap.buffer); - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - val->image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data ((char *) bitmap.buffer, - CAIRO_FORMAT_A8, - width, height, stride); - if (val->image == NULL) { - free (bitmap.buffer); - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - _cairo_image_surface_assume_ownership_of_data (val->image); - } - - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinate of the control box needs to be negated. - */ - - val->size.width = (unsigned short) width; - val->size.height = (unsigned short) height; - val->size.x = (short) (cbox.xMin >> 6); - val->size.y = - (short) (cbox.yMax >> 6); - - FAIL: - _ft_unscaled_font_unlock_face (unscaled); - - return status; -} - -const cairo_font_backend_t cairo_ft_font_backend = { - _cairo_ft_font_create, - _cairo_ft_font_destroy_font, - _cairo_ft_font_destroy_unscaled_font, - _cairo_ft_font_font_extents, - _cairo_ft_font_text_to_glyphs, - _cairo_ft_font_glyph_extents, - _cairo_ft_font_glyph_bbox, - _cairo_ft_font_show_glyphs, - _cairo_ft_font_glyph_path, - _cairo_ft_font_get_glyph_cache_key, - _cairo_ft_font_create_glyph -}; - -/* implement the platform-specific interface */ - -/** - * cairo_ft_font_create: - * @pattern: A fully resolved fontconfig - * pattern. A pattern can be resolved, by, among other things, calling - * FcConfigSubstitute(), FcDefaultSubstitute(), then - * FcFontMatch(). Cairo will call FcPatternReference() on this - * pattern, so you should not further modify the pattern, but you can - * release your reference to the pattern with FcPatternDestroy() if - * you no longer need to access it. - * @scale: The scale at which this font will be used. The - * scale is given by multiplying the font matrix (see - * cairo_transform_font()) by the current transformation matrix. - * The translation elements of the resulting matrix are ignored. - * - * Creates a new font for the FreeType font backend based on a - * fontconfig pattern. This font can then be used with - * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend - * specific functions like cairo_ft_font_lock_face(). - * - * Return value: a newly created #cairo_font_t. Free with - * cairo_font_destroy() when you are done using it. - **/ -cairo_font_t * -cairo_ft_font_create (FcPattern *pattern, - cairo_matrix_t *scale) -{ - cairo_font_scale_t sc; - double tx, ty; - - cairo_matrix_get_affine (scale, - &sc.matrix[0][0], &sc.matrix[0][1], - &sc.matrix[1][0], &sc.matrix[1][1], - &tx, &ty); - - return _ft_font_create (pattern, &sc); -} - -/** - * cairo_ft_font_create_for_ft_face: - * @face: A FreeType face object, already opened. This must - * be kept around until the font object's refcount drops to - * zero and it is freed. The font object can be kept alive by - * internal caching, so it's safest to keep the face object - * around forever. - * @load_flags: The flags to pass to FT_Load_Glyph when loading - * glyphs from the font. These flags control aspects of - * rendering such as hinting and antialiasing. See the FreeType - * docs for full information. - * @scale: The scale at which this font will be used. The - * scale is given by multiplying the font matrix (see - * cairo_transform_font()) by the current transformation matrix. - * The translation elements of the resulting matrix are ignored. - * - * Creates a new font forthe FreeType font backend from a pre-opened - * FreeType face. This font can then be used with cairo_set_font(), - * cairo_font_glyph_extents(), or FreeType backend specific - * functions like cairo_ft_font_lock_face() Cairo will determine the - * pixel size and transformation from the @scale parameter and call - * FT_Set_Transform() and FT_Set_Pixel_Sizes(). - * - * Return value: a newly created #cairo_font_t. Free with - * cairo_font_destroy() when you are done using it. - **/ -cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face, - int load_flags, - cairo_matrix_t *scale) -{ - cairo_ft_font_t *f = NULL; - ft_unscaled_font_t *unscaled = NULL; - cairo_font_scale_t sc; - double tx, ty; - - unscaled = _ft_unscaled_font_create_from_face (face); - if (unscaled == NULL) - return NULL; - - f = malloc (sizeof(cairo_ft_font_t)); - if (f == NULL) - goto FREE_UNSCALED; - - f->unscaled = unscaled; - f->pattern = NULL; - f->load_flags = load_flags; - - cairo_matrix_get_affine (scale, - &sc.matrix[0][0], &sc.matrix[0][1], - &sc.matrix[1][0], &sc.matrix[1][1], - &tx, &ty); - - _cairo_font_init ((cairo_font_t *)f, &sc, &cairo_ft_font_backend); - - return (cairo_font_t *)f; - - FREE_UNSCALED: - _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); - - return NULL; -} - - -/** - * cairo_ft_font_lock_face: - * @ft_font: A #cairo_font_t from the FreeType font backend. Such an - * object can be created with cairo_ft_font_create() or - * cairo_ft_font_create_for_ft_face(). On some platforms the font from - * cairo_current_font() will also be a FreeType font, but using this - * functionality with fonts you don't create yourself is not - * recommended. - * - * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType - * backend font and scales it appropriately for the font. You must - * release the face with cairo_ft_font_unlock_face() - * when you are done using it. Since the #FT_Face object can be - * shared between multiple #cairo_font_t objects, you must not - * lock any other font objects until you unlock this one. A count is - * kept of the number of times cairo_ft_font_lock_face() is - * called. cairo_ft_font_unlock_face() must be called the same number - * of times. - * - * You must be careful when using this function in a library or in a - * threaded application, because other threads may lock faces that - * share the same #FT_Face object. For this reason, you must call - * cairo_ft_lock() before locking any face objects, and - * cairo_ft_unlock() after you are done. (These functions are not yet - * implemented, so this function cannot be currently safely used in a - * threaded application.) - - * Return value: The #FT_Face object for @font, scaled appropriately. - **/ -FT_Face -cairo_ft_font_lock_face (cairo_font_t *abstract_font) -{ - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; - FT_Face face; - - face = _ft_unscaled_font_lock_face (font->unscaled); - if (!face) - return NULL; - - _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); - - return face; -} - -/** - * cairo_ft_font_unlock_face: - * @ft_font: A #cairo_font_t from the FreeType font backend. Such an - * object can be created with cairo_ft_font_create() or - * cairo_ft_font_create_for_ft_face(). On some platforms the font from - * cairo_current_font() will also be a FreeType font, but using this - * functionality with fonts you don't create yourself is not - * recommended. - * - * Releases a face obtained with cairo_ft_font_lock_face(). See the - * documentation for that function for full details. - **/ -void -cairo_ft_font_unlock_face (cairo_font_t *abstract_font) -{ - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; - - _ft_unscaled_font_unlock_face (font->unscaled); -} - -/** - * cairo_ft_font_get_pattern: - * @ft_font: A #cairo_font_t from the FreeType font backend. Such an - * object can be created with cairo_ft_font_create() or - * cairo_ft_font_create_for_ft_face(). On some platforms the font from - * cairo_current_font() will also be a FreeType font, but using this - * functionality with fonts you don't create yourself is not - * recommended. - * - * cairo_ft_font_get_pattern() gets the #FcPattern for a FreeType - * backend font. - - * Return value: The #FcPattenr for @font. The return value is owned - * by the font, so you must not modify it, and must call - * FcPatternReference() to keep a persistant reference to the - * pattern. If the font was created with cairo_ft_font_create_for_ft_face() - * returns %NULL. - **/ -FcPattern * -cairo_ft_font_get_pattern (cairo_font_t *abstract_font) -{ - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; - - if (font == NULL) - return NULL; - - return font->pattern; -} - -/* We expose our unscaled font implementation internally for the the - * PDF backend, which needs to keep track of the the different - * fonts-on-disk used by a document, so it can embed them. - */ -cairo_unscaled_font_t * -_cairo_ft_font_get_unscaled_font (cairo_font_t *abstract_font) -{ - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; - - return (cairo_unscaled_font_t *)font->unscaled; -} - -/* This differs from _cairo_ft_scaled_font_lock_face in that it doesn't - * set the scale on the face, but just returns it at the last scale. - */ -FT_Face -_cairo_ft_unscaled_font_lock_face (cairo_unscaled_font_t *unscaled_font) -{ - return _ft_unscaled_font_lock_face ((ft_unscaled_font_t *)unscaled_font); -} - -void -_cairo_ft_unscaled_font_unlock_face (cairo_unscaled_font_t *unscaled_font) -{ - _ft_unscaled_font_unlock_face ((ft_unscaled_font_t *)unscaled_font); -} diff --git a/src/cairo_glitz_surface.c b/src/cairo_glitz_surface.c deleted file mode 100644 index ee664e1cc..000000000 --- a/src/cairo_glitz_surface.c +++ /dev/null @@ -1,1317 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 David Reveman - * - * Permission to use, copy, modify, distribute, and sell this software - * and its documentation for any purpose is hereby granted without - * fee, provided that the above copyright notice appear in all copies - * and that both that copyright notice and this permission notice - * appear in supporting documentation, and that the name of David - * Reveman not be used in advertising or publicity pertaining to - * distribution of the software without specific, written prior - * permission. David Reveman makes no representations about the - * suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author: David Reveman <davidr@novell.com> - */ - -#include "cairoint.h" -#include "cairo-glitz.h" - -void -cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface) -{ - cairo_surface_t *crsurface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - crsurface = cairo_glitz_surface_create (surface); - if (crsurface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, crsurface); - - cairo_surface_destroy (crsurface); -} - -typedef struct _cairo_glitz_surface { - cairo_surface_t base; - - glitz_surface_t *surface; - glitz_format_t *format; - pixman_region16_t *clip; -} cairo_glitz_surface_t; - -static void -_cairo_glitz_surface_destroy (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (surface->clip) - { - glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - pixman_region_destroy (surface->clip); - } - - glitz_surface_destroy (surface->surface); - free (surface); -} - -static glitz_format_name_t -_glitz_format (cairo_format_t format) -{ - switch (format) { - default: - case CAIRO_FORMAT_ARGB32: - return GLITZ_STANDARD_ARGB32; - case CAIRO_FORMAT_RGB24: - return GLITZ_STANDARD_RGB24; - case CAIRO_FORMAT_A8: - return GLITZ_STANDARD_A8; - case CAIRO_FORMAT_A1: - return GLITZ_STANDARD_A1; - } -} - -static cairo_surface_t * -_cairo_glitz_surface_create_similar (void *abstract_src, - cairo_format_t format, - int draw, - int width, - int height) -{ - cairo_glitz_surface_t *src = abstract_src; - cairo_surface_t *crsurface; - glitz_drawable_t *drawable; - glitz_surface_t *surface; - glitz_format_t *gformat; - - drawable = glitz_surface_get_drawable (src->surface); - - gformat = glitz_find_standard_format (drawable, _glitz_format (format)); - if (!gformat) - return NULL; - - surface = glitz_surface_create (drawable, gformat, width, height, 0, NULL); - if (!surface) - return NULL; - - crsurface = cairo_glitz_surface_create (surface); - - glitz_surface_destroy (surface); - - return crsurface; -} - -static double -_cairo_glitz_surface_pixels_per_inch (void *abstract_surface) -{ - return 96.0; -} - -static cairo_status_t -_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, - cairo_rectangle_t *interest, - cairo_image_surface_t **image_out, - cairo_rectangle_t *rect_out) -{ - cairo_image_surface_t *image; - int x1, y1, x2, y2; - int width, height; - char *pixels; - cairo_format_masks_t format; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - - x1 = 0; - y1 = 0; - x2 = glitz_surface_get_width (surface->surface); - y2 = glitz_surface_get_height (surface->surface); - - if (interest) - { - if (interest->x > x1) - x1 = interest->x; - if (interest->y > y1) - y1 = interest->y; - if (interest->x + interest->width < x2) - x2 = interest->x + interest->width; - if (interest->y + interest->height < y2) - y2 = interest->y + interest->height; - - if (x1 >= x2 || y1 >= y2) - { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - width = x2 - x1; - height = y2 - y1; - - if (rect_out) - { - rect_out->x = x1; - rect_out->y = y1; - rect_out->width = width; - rect_out->height = height; - } - - if (surface->format->type == GLITZ_FORMAT_TYPE_COLOR) { - if (surface->format->color.red_size > 0) { - format.bpp = 32; - - if (surface->format->color.alpha_size > 0) - format.alpha_mask = 0xff000000; - else - format.alpha_mask = 0x0; - - format.red_mask = 0xff0000; - format.green_mask = 0xff00; - format.blue_mask = 0xff; - } else { - format.bpp = 8; - format.blue_mask = format.green_mask = format.red_mask = 0x0; - format.alpha_mask = 0xff; - } - } else { - format.bpp = 32; - format.alpha_mask = 0xff000000; - format.red_mask = 0xff0000; - format.green_mask = 0xff00; - format.blue_mask = 0xff; - } - - pf.masks.bpp = format.bpp; - pf.masks.alpha_mask = format.alpha_mask; - pf.masks.red_mask = format.red_mask; - pf.masks.green_mask = format.green_mask; - pf.masks.blue_mask = format.blue_mask; - pf.xoffset = 0; - pf.skip_lines = 0; - - /* XXX: we should eventually return images with negative stride, - need to verify that libpixman have no problem with this first. */ - pf.bytes_per_line = (((width * format.bpp) / 8) + 3) & -4; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - - pixels = malloc (height * pf.bytes_per_line); - if (!pixels) - return CAIRO_STATUS_NO_MEMORY; - - buffer = glitz_buffer_create_for_data (pixels); - if (!buffer) { - free (pixels); - return CAIRO_STATUS_NO_MEMORY; - } - - glitz_get_pixels (surface->surface, - x1, y1, - width, height, - &pf, - buffer); - - glitz_buffer_destroy (buffer); - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_masks (pixels, - &format, - width, height, - pf.bytes_per_line); - - if (!image) - { - free (pixels); - return CAIRO_STATUS_NO_MEMORY; - } - - _cairo_image_surface_assume_ownership_of_data (image); - - _cairo_image_surface_set_repeat (image, surface->base.repeat); - _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - - *image_out = image; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image, - int x_dst, - int y_dst) -{ - cairo_glitz_surface_t *surface = abstract_surface; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - pixman_format_t *format; - int am, rm, gm, bm; - char *data; - - format = pixman_image_get_format (image->pixman_image); - if (!format) - return CAIRO_STATUS_NO_MEMORY; - - pixman_format_get_masks (format, &pf.masks.bpp, &am, &rm, &gm, &bm); - - pf.masks.alpha_mask = am; - pf.masks.red_mask = rm; - pf.masks.green_mask = gm; - pf.masks.blue_mask = bm; - pf.xoffset = 0; - pf.skip_lines = 0; - - /* check for negative stride */ - if (image->stride < 0) - { - pf.bytes_per_line = -image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; - data = (char *) image->data + image->stride * (image->height - 1); - } - else - { - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - data = (char *) image->data; - } - - buffer = glitz_buffer_create_for_data (data); - if (!buffer) - return CAIRO_STATUS_NO_MEMORY; - - glitz_set_pixels (surface->surface, - x_dst, y_dst, - image->width, image->height, - &pf, - buffer); - - glitz_buffer_destroy (buffer); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - *image_extra = NULL; - - return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); -} - -static void -_cairo_glitz_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_glitz_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect_out, - void **image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, - image_rect_out); - if (status) - return status; - - *image_out = image; - *image_extra = NULL; - - return status; -} - -static void -_cairo_glitz_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - _cairo_glitz_surface_set_image (surface, image, - image_rect->x, image_rect->y); - - cairo_surface_destroy (&image->base); -} - - -static cairo_status_t -_cairo_glitz_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_glitz_surface_t *clone; - - if (src->backend == surface->base.backend) - { - *clone_out = src; - cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - else if (_cairo_surface_is_image (src)) - { - cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - - clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (surface, image_src->format, 0, - image_src->width, - image_src->height); - if (!clone) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_glitz_surface_set_image (clone, image_src, 0, 0); - - *clone_out = &clone->base; - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, - cairo_matrix_t *matrix) -{ - glitz_transform_t transform; - - transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - - transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - - transform.matrix[2][0] = 0; - transform.matrix[2][1] = 0; - transform.matrix[2][2] = 1 << 16; - - glitz_surface_set_transform (surface->surface, &transform); -} - -static glitz_operator_t -_glitz_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return GLITZ_OPERATOR_CLEAR; - case CAIRO_OPERATOR_SRC: - return GLITZ_OPERATOR_SRC; - case CAIRO_OPERATOR_DST: - return GLITZ_OPERATOR_DST; - case CAIRO_OPERATOR_OVER_REVERSE: - return GLITZ_OPERATOR_OVER_REVERSE; - case CAIRO_OPERATOR_IN: - return GLITZ_OPERATOR_IN; - case CAIRO_OPERATOR_IN_REVERSE: - return GLITZ_OPERATOR_IN_REVERSE; - case CAIRO_OPERATOR_OUT: - return GLITZ_OPERATOR_OUT; - case CAIRO_OPERATOR_OUT_REVERSE: - return GLITZ_OPERATOR_OUT_REVERSE; - case CAIRO_OPERATOR_ATOP: - return GLITZ_OPERATOR_ATOP; - case CAIRO_OPERATOR_ATOP_REVERSE: - return GLITZ_OPERATOR_ATOP_REVERSE; - case CAIRO_OPERATOR_XOR: - return GLITZ_OPERATOR_XOR; - case CAIRO_OPERATOR_ADD: - return GLITZ_OPERATOR_ADD; - case CAIRO_OPERATOR_OVER: - default: - return GLITZ_OPERATOR_OVER; - } -} - -static glitz_status_t -_glitz_ensure_target (glitz_surface_t *surface) -{ - glitz_drawable_t *drawable; - - drawable = glitz_surface_get_attached_drawable (surface); - if (!drawable) { - glitz_drawable_format_t *dformat; - glitz_drawable_format_t templ; - glitz_format_t *format; - glitz_drawable_t *pbuffer; - unsigned long mask; - int i; - - format = glitz_surface_get_format (surface); - if (format->type != GLITZ_FORMAT_TYPE_COLOR) - return CAIRO_INT_STATUS_UNSUPPORTED; - - drawable = glitz_surface_get_drawable (surface); - dformat = glitz_drawable_get_format (drawable); - - templ.types.pbuffer = 1; - mask = GLITZ_FORMAT_PBUFFER_MASK; - - templ.samples = dformat->samples; - mask |= GLITZ_FORMAT_SAMPLES_MASK; - - i = 0; - do { - dformat = glitz_find_similar_drawable_format (drawable, - mask, &templ, i++); - - if (dformat) { - int sufficient = 1; - - if (format->color.red_size) { - if (dformat->color.red_size < format->color.red_size) - sufficient = 0; - } - if (format->color.alpha_size) { - if (dformat->color.alpha_size < format->color.alpha_size) - sufficient = 0; - } - - if (sufficient) - break; - } - } while (dformat); - - if (!dformat) - return CAIRO_INT_STATUS_UNSUPPORTED; - - pbuffer = - glitz_create_pbuffer_drawable (drawable, dformat, - glitz_surface_get_width (surface), - glitz_surface_get_height (surface)); - if (!pbuffer) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glitz_surface_attach (surface, pbuffer, - GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, - 0, 0); - - glitz_drawable_destroy (pbuffer); - } - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct _cairo_glitz_surface_attributes { - cairo_surface_attributes_t base; - - glitz_fill_t fill; - glitz_filter_t filter; - glitz_fixed16_16_t *params; - int n_params; - cairo_bool_t acquired; -} cairo_glitz_surface_attributes_t; - -static cairo_int_status_t -_cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, - cairo_glitz_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_glitz_surface_t **surface_out, - cairo_glitz_surface_attributes_t *attr) -{ - cairo_glitz_surface_t *src = NULL; - - attr->acquired = FALSE; - - switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - glitz_drawable_t *drawable; - glitz_fixed16_16_t *params; - int n_params; - int i; - unsigned short alpha; - - /* XXX: the current color gradient acceleration provided by glitz is - * experimental, it's been proven inappropriate in a number of ways, - * most importantly, it's currently implemented as filters and - * gradients are not filters. eventually, it will be replaced with - * something more appropriate. - */ - - if (gradient->n_stops < 2) - break; - - /* glitz doesn't support inner and outer circle with different - center points. */ - if (pattern->type == CAIRO_PATTERN_RADIAL) - { - cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; - - if (grad->center0.x != grad->center1.x || - grad->center0.y != grad->center1.y) - break; - } - - drawable = glitz_surface_get_drawable (dst->surface); - if (!(glitz_drawable_get_features (drawable) & - GLITZ_FEATURE_FRAGMENT_PROGRAM_MASK)) - break; - - if (pattern->filter != CAIRO_FILTER_BILINEAR && - pattern->filter != CAIRO_FILTER_GOOD && - pattern->filter != CAIRO_FILTER_BEST) - break; - - alpha = (gradient->stops[0].color.alpha * pattern->alpha) * 0xffff; - for (i = 1; i < gradient->n_stops; i++) - { - unsigned short a; - - a = (gradient->stops[i].color.alpha * pattern->alpha) * 0xffff; - if (a != alpha) - break; - } - - /* we can't have color stops with different alpha as gradient color - interpolation should be done to unpremultiplied colors. */ - if (i < gradient->n_stops) - break; - - n_params = gradient->n_stops * 3 + 4; - - params = malloc (sizeof (glitz_fixed16_16_t) * n_params); - if (!params) - return CAIRO_STATUS_NO_MEMORY; - - src = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_scratch (&dst->base, - CAIRO_FORMAT_ARGB32, 0, - gradient->n_stops, 1); - if (!src) - { - free (params); - return CAIRO_STATUS_NO_MEMORY; - } - - for (i = 0; i < gradient->n_stops; i++) { - glitz_color_t color; - - color.red = gradient->stops[i].color.red * alpha; - color.green = gradient->stops[i].color.green * alpha; - color.blue = gradient->stops[i].color.blue * alpha; - color.alpha = alpha; - - glitz_set_rectangle (src->surface, &color, i, 0, 1, 1); - - params[4 + 3 * i] = gradient->stops[i].offset; - params[5 + 3 * i] = i << 16; - params[6 + 3 * i] = 0; - } - - if (pattern->type == CAIRO_PATTERN_LINEAR) - { - cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; - - params[0] = _cairo_fixed_from_double (grad->point0.x); - params[1] = _cairo_fixed_from_double (grad->point0.y); - params[2] = _cairo_fixed_from_double (grad->point1.x); - params[3] = _cairo_fixed_from_double (grad->point1.y); - attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; - } - else - { - cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; - - params[0] = _cairo_fixed_from_double (grad->center0.x); - params[1] = _cairo_fixed_from_double (grad->center0.y); - params[2] = _cairo_fixed_from_double (grad->radius0); - params[3] = _cairo_fixed_from_double (grad->radius1); - attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; - } - - switch (pattern->extend) { - case CAIRO_EXTEND_NONE: - attr->fill = GLITZ_FILL_NEAREST; - break; - case CAIRO_EXTEND_REPEAT: - attr->fill = GLITZ_FILL_REPEAT; - break; - case CAIRO_EXTEND_REFLECT: - attr->fill = GLITZ_FILL_REFLECT; - break; - } - - attr->params = params; - attr->n_params = n_params; - attr->base.matrix = pattern->matrix; - attr->base.x_offset = 0; - attr->base.y_offset = 0; - } break; - default: - break; - } - - if (!src) - { - cairo_int_status_t status; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - x, y, width, height, - (cairo_surface_t **) &src, - &attr->base); - if (status) - return status; - - if (src) - { - switch (attr->base.extend) { - case CAIRO_EXTEND_NONE: - attr->fill = GLITZ_FILL_TRANSPARENT; - break; - case CAIRO_EXTEND_REPEAT: - attr->fill = GLITZ_FILL_REPEAT; - break; - case CAIRO_EXTEND_REFLECT: - attr->fill = GLITZ_FILL_REFLECT; - break; - } - - switch (attr->base.filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - attr->filter = GLITZ_FILTER_NEAREST; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - default: - attr->filter = GLITZ_FILTER_BILINEAR; - break; - } - - attr->params = NULL; - attr->n_params = 0; - attr->acquired = TRUE; - } - } - - *surface_out = src; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_pattern_release_surface (cairo_glitz_surface_t *dst, - cairo_glitz_surface_t *surface, - cairo_glitz_surface_attributes_t *attr) -{ - if (attr->acquired) - _cairo_pattern_release_surface (&dst->base, &surface->base, - &attr->base); - else - _cairo_glitz_surface_destroy (surface); -} - -static cairo_int_status_t -_cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, - cairo_pattern_t *mask, - cairo_glitz_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_glitz_surface_t **src_out, - cairo_glitz_surface_t **mask_out, - cairo_glitz_surface_attributes_t *sattr, - cairo_glitz_surface_attributes_t *mattr) -{ - cairo_int_status_t status; - cairo_pattern_union_t tmp; - cairo_bool_t src_opaque, mask_opaque; - double src_alpha, mask_alpha; - - src_opaque = _cairo_pattern_is_opaque (src); - mask_opaque = !mask || _cairo_pattern_is_opaque (mask); - - /* For surface patterns, we move any translucency from src->alpha - * to mask->alpha so we can use the source unchanged. Otherwise we - * move the translucency from mask->alpha to src->alpha so that - * we can drop the mask if possible. - */ - if (src->type == CAIRO_PATTERN_SURFACE) - { - if (mask) { - mask_opaque = mask_opaque && src_opaque; - mask_alpha = mask->alpha * src->alpha; - } else { - mask_opaque = src_opaque; - mask_alpha = src->alpha; - } - - src_alpha = 1.0; - src_opaque = TRUE; - } - else - { - if (mask) - { - src_opaque = mask_opaque && src_opaque; - src_alpha = mask->alpha * src->alpha; - /* FIXME: This needs changing when we support RENDER - * style 4-channel masks. - */ - if (mask->type == CAIRO_PATTERN_SOLID) - mask = NULL; - } else - src_alpha = src->alpha; - - mask_alpha = 1.0; - mask_opaque = TRUE; - } - - _cairo_pattern_init_copy (&tmp.base, src); - _cairo_pattern_set_alpha (&tmp.base, src_alpha); - - status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, - src_x, src_y, - width, height, - src_out, sattr); - - _cairo_pattern_fini (&tmp.base); - - if (status) - return status; - - if (mask || !mask_opaque) - { - if (mask) - _cairo_pattern_init_copy (&tmp.base, mask); - else - _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); - - _cairo_pattern_set_alpha (&tmp.base, mask_alpha); - - status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, - mask_x, mask_y, - width, height, - mask_out, mattr); - - _cairo_pattern_fini (&tmp.base); - - if (status) - { - _cairo_glitz_pattern_release_surface (dst, *src_out, sattr); - return status; - } - } - else - { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, - cairo_glitz_surface_attributes_t *a) -{ - _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); - glitz_surface_set_fill (surface->surface, a->fill); - glitz_surface_set_filter (surface->surface, a->filter, - a->params, a->n_params); -} - -static cairo_int_status_t -_cairo_glitz_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_glitz_surface_attributes_t src_attr, mask_attr; - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - cairo_glitz_surface_t *mask; - cairo_int_status_t status; - - if (op == CAIRO_OPERATOR_SATURATE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, - dst, - src_x, src_y, - mask_x, mask_y, - width, height, - &src, &mask, - &src_attr, &mask_attr); - if (status) - return status; - - _cairo_glitz_surface_set_attributes (src, &src_attr); - if (mask) - { - _cairo_glitz_surface_set_attributes (mask, &mask_attr); - glitz_composite (_glitz_operator (op), - src->surface, - mask->surface, - dst->surface, - src_x + src_attr.base.x_offset, - src_y + src_attr.base.y_offset, - mask_x + mask_attr.base.x_offset, - mask_y + mask_attr.base.y_offset, - dst_x, dst_y, - width, height); - - if (mask_attr.n_params) - free (mask_attr.params); - - _cairo_glitz_pattern_release_surface (dst, mask, &mask_attr); - } - else - { - glitz_composite (_glitz_operator (op), - src->surface, - NULL, - dst->surface, - src_x + src_attr.base.x_offset, - src_y + src_attr.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - - if (src_attr.n_params) - free (src_attr.params); - - _cairo_glitz_pattern_release_surface (dst, src, &src_attr); - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int n_rects) -{ - cairo_glitz_surface_t *dst = abstract_dst; - - if (op == CAIRO_OPERATOR_SRC) - { - glitz_color_t glitz_color; - - glitz_color.red = color->red_short; - glitz_color.green = color->green_short; - glitz_color.blue = color->blue_short; - glitz_color.alpha = color->alpha_short; - - if (glitz_surface_get_width (dst->surface) != 1 || - glitz_surface_get_height (dst->surface) != 1) - _glitz_ensure_target (dst->surface); - - glitz_set_rectangles (dst->surface, &glitz_color, - (glitz_rectangle_t *) rects, n_rects); - } - else - { - cairo_glitz_surface_t *src; - - if (op == CAIRO_OPERATOR_SATURATE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - src = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_solid (&dst->base, - CAIRO_FORMAT_ARGB32, 1, 1, - (cairo_color_t *) color); - if (!src) - return CAIRO_STATUS_NO_MEMORY; - - while (n_rects--) - { - glitz_composite (_glitz_operator (op), - src->surface, - NULL, - dst->surface, - 0, 0, - 0, 0, - rects->x, rects->y, - rects->width, rects->height); - rects++; - } - - cairo_surface_destroy (&src->base); - } - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int n_traps) -{ - cairo_glitz_surface_attributes_t attributes; - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - cairo_glitz_surface_t *mask = NULL; - glitz_buffer_t *buffer = NULL; - void *data = NULL; - cairo_int_status_t status; - unsigned short alpha; - - if (op == CAIRO_OPERATOR_SATURATE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern->type == CAIRO_PATTERN_SURFACE) - { - cairo_pattern_union_t tmp; - - _cairo_pattern_init_copy (&tmp.base, pattern); - _cairo_pattern_set_alpha (&tmp.base, 1.0); - - status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, - src_x, src_y, - width, height, - &src, &attributes); - - _cairo_pattern_fini (&tmp.base); - - alpha = pattern->alpha * 0xffff; - } - else - { - status = _cairo_glitz_pattern_acquire_surface (pattern, dst, - src_x, src_y, - width, height, - &src, &attributes); - alpha = 0xffff; - } - - if (status) - return status; - - if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) - { - static glitz_color_t clear_black = { 0, 0, 0, 0 }; - glitz_color_t color; - glitz_geometry_format_t format; - int n_trap_added; - int offset = 0; - int data_size = 0; - int size = 30 * n_traps; /* just a guess */ - - format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; - format.vertex.type = GLITZ_DATA_TYPE_FLOAT; - format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); - format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; - format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; - format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; - format.vertex.mask.offset = 2 * sizeof (glitz_float_t); - - mask = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_FORMAT_A8, 0, - 2, 1); - if (!mask) - { - _cairo_glitz_pattern_release_surface (dst, src, &attributes); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - color.red = color.green = color.blue = color.alpha = alpha; - - glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); - glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); - - glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); - glitz_surface_set_filter (mask->surface, - GLITZ_FILTER_BILINEAR, - NULL, 0); - - size *= format.vertex.bytes_per_vertex; - - while (n_traps) - { - if (data_size < size) - { - data_size = size; - data = realloc (data, data_size); - if (!data) - { - _cairo_glitz_pattern_release_surface (dst, src, - &attributes); - return CAIRO_STATUS_NO_MEMORY; - } - - if (buffer) - glitz_buffer_destroy (buffer); - - buffer = glitz_buffer_create_for_data (data); - if (!buffer) { - free (data); - _cairo_glitz_pattern_release_surface (dst, src, - &attributes); - return CAIRO_STATUS_NO_MEMORY; - } - } - - offset += - glitz_add_trapezoids (buffer, - offset, size - offset, - format.vertex.type, mask->surface, - (glitz_trapezoid_t *) traps, n_traps, - &n_trap_added); - - n_traps -= n_trap_added; - traps += n_trap_added; - size *= 2; - } - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_VERTEX, - &format, buffer); - glitz_set_array (dst->surface, 0, 3, - offset / format.vertex.bytes_per_vertex, - 0, 0); - } - else - { - cairo_image_surface_t *image; - char *ptr; - int stride; - - stride = (width + 3) & -4; - data = malloc (stride * height); - if (!data) - { - _cairo_glitz_pattern_release_surface (dst, src, &attributes); - return CAIRO_STATUS_NO_MEMORY; - } - - memset (data, 0, stride * height); - - /* using negative stride */ - ptr = (char *) data + stride * (height - 1); - - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (ptr, - CAIRO_FORMAT_A8, - width, height, - -stride); - if (!image) - { - cairo_surface_destroy (&src->base); - free (data); - return CAIRO_STATUS_NO_MEMORY; - } - - pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, - (pixman_trapezoid_t *) traps, n_traps); - - if (alpha != 0xffff) - { - pixman_color_t color; - - color.red = color.green = color.blue = color.alpha = alpha; - - pixman_fill_rectangle (PIXMAN_OPERATOR_IN, - image->pixman_image, - &color, - 0, 0, width, height); - } - - mask = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_scratch (&dst->base, - CAIRO_FORMAT_A8, 0, - width, height); - if (!mask) - { - _cairo_glitz_pattern_release_surface (dst, src, &attributes); - free (data); - cairo_surface_destroy (&image->base); - return CAIRO_STATUS_NO_MEMORY; - } - - _cairo_glitz_surface_set_image (mask, image, 0, 0); - } - - _cairo_glitz_surface_set_attributes (src, &attributes); - - glitz_composite (_glitz_operator (op), - src->surface, - mask->surface, - dst->surface, - src_x + attributes.base.x_offset, - src_y + attributes.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - - if (attributes.n_params) - free (attributes.params); - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_NONE, - NULL, NULL); - - if (buffer) - glitz_buffer_destroy (buffer); - - free (data); - - _cairo_glitz_pattern_release_surface (dst, src, &attributes); - if (mask) - cairo_surface_destroy (&mask->base); - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_glitz_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_glitz_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (region) - { - glitz_box_t *box; - int n; - - if (!surface->clip) - { - surface->clip = pixman_region_create (); - if (!surface->clip) - return CAIRO_STATUS_NO_MEMORY; - } - pixman_region_copy (surface->clip, region); - - box = (glitz_box_t *) pixman_region_rects (surface->clip); - n = pixman_region_num_rects (surface->clip); - glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); - } - else - { - glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - - if (surface->clip) - pixman_region_destroy (surface->clip); - - surface->clip = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_glitz_surface_backend = { - _cairo_glitz_surface_create_similar, - _cairo_glitz_surface_destroy, - _cairo_glitz_surface_pixels_per_inch, - _cairo_glitz_surface_acquire_source_image, - _cairo_glitz_surface_release_source_image, - _cairo_glitz_surface_acquire_dest_image, - _cairo_glitz_surface_release_dest_image, - _cairo_glitz_surface_clone_similar, - _cairo_glitz_surface_composite, - _cairo_glitz_surface_fill_rectangles, - _cairo_glitz_surface_composite_trapezoids, - _cairo_glitz_surface_copy_page, - _cairo_glitz_surface_show_page, - _cairo_glitz_surface_set_clip_region, - NULL /* show_glyphs */ -}; - -cairo_surface_t * -cairo_glitz_surface_create (glitz_surface_t *surface) -{ - cairo_glitz_surface_t *crsurface; - - if (!surface) - return NULL; - - crsurface = malloc (sizeof (cairo_glitz_surface_t)); - if (crsurface == NULL) - return NULL; - - _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend); - - glitz_surface_reference (surface); - - crsurface->surface = surface; - crsurface->format = glitz_surface_get_format (surface); - crsurface->clip = NULL; - - return (cairo_surface_t *) crsurface; -} diff --git a/src/cairo_gstate.c b/src/cairo_gstate.c deleted file mode 100644 index d6db560a3..000000000 --- a/src/cairo_gstate.c +++ /dev/null @@ -1,2566 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include <stdlib.h> -#include <math.h> - -#include "cairoint.h" - -static cairo_status_t -_cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_pattern_t *src, - cairo_operator_t operator, - cairo_surface_t *dst, - cairo_traps_t *traps); - -static cairo_status_t -_cairo_gstate_ensure_font (cairo_gstate_t *gstate); - -static void -_cairo_gstate_unset_font (cairo_gstate_t *gstate); - -cairo_gstate_t * -_cairo_gstate_create () -{ - cairo_status_t status; - cairo_gstate_t *gstate; - - gstate = malloc (sizeof (cairo_gstate_t)); - - if (gstate) - { - status = _cairo_gstate_init (gstate); - if (status) { - free (gstate); - return NULL; - } - } - - return gstate; -} - -cairo_status_t -_cairo_gstate_init (cairo_gstate_t *gstate) -{ - gstate->operator = CAIRO_GSTATE_OPERATOR_DEFAULT; - - gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; - - gstate->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; - gstate->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; - gstate->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; - gstate->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT; - - gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; - - gstate->dash = NULL; - gstate->num_dashes = 0; - gstate->dash_offset = 0.0; - - gstate->font_family = NULL; - gstate->font_slant = CAIRO_FONT_SLANT_DEFAULT; - gstate->font_weight = CAIRO_FONT_WEIGHT_DEFAULT; - - gstate->font = NULL; - - gstate->surface = NULL; - - gstate->clip.region = NULL; - gstate->clip.surface = NULL; - - gstate->pattern = _cairo_pattern_create_solid (0.0, 0.0, 0.0); - if (!gstate->pattern) - return CAIRO_STATUS_NO_MEMORY; - - gstate->alpha = 1.0; - - gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; - _cairo_gstate_default_matrix (gstate); - - _cairo_path_init (&gstate->path); - - _cairo_pen_init_empty (&gstate->pen_regular); - - gstate->next = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) -{ - cairo_status_t status; - cairo_gstate_t *next; - - /* Copy all members, but don't smash the next pointer */ - next = gstate->next; - *gstate = *other; - gstate->next = next; - - /* Now fix up pointer data that needs to be cloned/referenced */ - if (other->dash) { - gstate->dash = malloc (other->num_dashes * sizeof (double)); - if (gstate->dash == NULL) - return CAIRO_STATUS_NO_MEMORY; - memcpy (gstate->dash, other->dash, other->num_dashes * sizeof (double)); - } - - if (other->font_family) { - gstate->font_family = strdup (other->font_family); - if (!gstate->font_family) - goto CLEANUP_DASH; - } - - if (other->font) { - gstate->font = other->font; - cairo_font_reference (gstate->font); - } - - if (other->clip.region) - { - gstate->clip.region = pixman_region_create (); - pixman_region_copy (gstate->clip.region, other->clip.region); - } - - cairo_surface_reference (gstate->surface); - cairo_surface_reference (gstate->clip.surface); - - cairo_pattern_reference (gstate->pattern); - - status = _cairo_path_init_copy (&gstate->path, &other->path); - if (status) - goto CLEANUP_FONT; - - status = _cairo_pen_init_copy (&gstate->pen_regular, &other->pen_regular); - if (status) - goto CLEANUP_PATH; - - return status; - - CLEANUP_PATH: - _cairo_path_fini (&gstate->path); - - CLEANUP_FONT: - cairo_font_destroy (gstate->font); - gstate->font = NULL; - - if (gstate->font_family) { - free (gstate->font_family); - gstate->font_family = NULL; - } - - CLEANUP_DASH: - free (gstate->dash); - gstate->dash = NULL; - - return CAIRO_STATUS_NO_MEMORY; -} - -void -_cairo_gstate_fini (cairo_gstate_t *gstate) -{ - if (gstate->font_family) - free (gstate->font_family); - - if (gstate->font) - cairo_font_destroy (gstate->font); - - if (gstate->surface) - cairo_surface_destroy (gstate->surface); - gstate->surface = NULL; - - if (gstate->clip.surface) - cairo_surface_destroy (gstate->clip.surface); - gstate->clip.surface = NULL; - - if (gstate->clip.region) - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = NULL; - - cairo_pattern_destroy (gstate->pattern); - - _cairo_matrix_fini (&gstate->font_matrix); - - _cairo_matrix_fini (&gstate->ctm); - _cairo_matrix_fini (&gstate->ctm_inverse); - - _cairo_path_fini (&gstate->path); - - _cairo_pen_fini (&gstate->pen_regular); - - if (gstate->dash) { - free (gstate->dash); - gstate->dash = NULL; - } -} - -void -_cairo_gstate_destroy (cairo_gstate_t *gstate) -{ - _cairo_gstate_fini (gstate); - free (gstate); -} - -cairo_gstate_t* -_cairo_gstate_clone (cairo_gstate_t *gstate) -{ - cairo_status_t status; - cairo_gstate_t *clone; - - clone = malloc (sizeof (cairo_gstate_t)); - if (clone) { - status = _cairo_gstate_init_copy (clone, gstate); - if (status) { - free (clone); - return NULL; - } - } - clone->next = NULL; - - return clone; -} - -cairo_status_t -_cairo_gstate_copy (cairo_gstate_t *dest, cairo_gstate_t *src) -{ - cairo_status_t status; - cairo_gstate_t *next; - - /* Preserve next pointer over fini/init */ - next = dest->next; - _cairo_gstate_fini (dest); - status = _cairo_gstate_init_copy (dest, src); - dest->next = next; - - return status; -} - -/* Push rendering off to an off-screen group. */ -/* XXX: Rethinking this API -cairo_status_t -_cairo_gstate_begin_group (cairo_gstate_t *gstate) -{ - Pixmap pix; - cairo_color_t clear; - unsigned int width, height; - - gstate->parent_surface = gstate->surface; - - width = _cairo_surface_get_width (gstate->surface); - height = _cairo_surface_get_height (gstate->surface); - - pix = XCreatePixmap (gstate->dpy, - _cairo_surface_get_drawable (gstate->surface), - width, height, - _cairo_surface_get_depth (gstate->surface)); - if (pix == 0) - return CAIRO_STATUS_NO_MEMORY; - - gstate->surface = cairo_surface_create (gstate->dpy); - if (gstate->surface == NULL) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_surface_set_drawableWH (gstate->surface, pix, width, height); - - _cairo_color_init (&clear); - _cairo_color_set_alpha (&clear, 0); - - status = _cairo_surface_fill_rectangle (gstate->surface, - CAIRO_OPERATOR_SRC, - &clear, - 0, 0, - _cairo_surface_get_width (gstate->surface), - _cairo_surface_get_height (gstate->surface)); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} -*/ - -/* Complete the current offscreen group, composing its contents onto the parent surface. */ -/* XXX: Rethinking this API -cairo_status_t -_cairo_gstate_end_group (cairo_gstate_t *gstate) -{ - Pixmap pix; - cairo_color_t mask_color; - cairo_surface_t mask; - - if (gstate->parent_surface == NULL) - return CAIRO_STATUS_INVALID_POP_GROUP; - - _cairo_surface_init (&mask, gstate->dpy); - _cairo_color_init (&mask_color); - _cairo_color_set_alpha (&mask_color, gstate->alpha); - - _cairo_surface_set_solid_color (&mask, &mask_color); - - * XXX: This could be made much more efficient by using - _cairo_surface_get_damaged_width/Height if cairo_surface_t actually kept - track of such informaton. * - _cairo_surface_composite (gstate->operator, - gstate->surface, - mask, - gstate->parent_surface, - 0, 0, - 0, 0, - 0, 0, - _cairo_surface_get_width (gstate->surface), - _cairo_surface_get_height (gstate->surface)); - - _cairo_surface_fini (&mask); - - pix = _cairo_surface_get_drawable (gstate->surface); - XFreePixmap (gstate->dpy, pix); - - cairo_surface_destroy (gstate->surface); - gstate->surface = gstate->parent_surface; - gstate->parent_surface = NULL; - - return CAIRO_STATUS_SUCCESS; -} -*/ - -cairo_status_t -_cairo_gstate_set_target_surface (cairo_gstate_t *gstate, cairo_surface_t *surface) -{ - double scale; - - _cairo_gstate_unset_font (gstate); - - if (gstate->surface) - cairo_surface_destroy (gstate->surface); - - gstate->surface = surface; - - /* Sometimes the user wants to return to having no target surface, - * (just like after cairo_create). This can be useful for forcing - * the old surface to be destroyed. */ - if (surface == NULL) - return CAIRO_STATUS_SUCCESS; - - cairo_surface_reference (gstate->surface); - - scale = _cairo_surface_pixels_per_inch (surface) / gstate->pixels_per_inch; - _cairo_gstate_scale (gstate, scale, scale); - gstate->pixels_per_inch = _cairo_surface_pixels_per_inch (surface); - - return CAIRO_STATUS_SUCCESS; -} - -/* XXX: Need to decide the memory mangement semantics of this - function. Should it reference the surface again? */ -cairo_surface_t * -_cairo_gstate_current_target_surface (cairo_gstate_t *gstate) -{ - if (gstate == NULL) - return NULL; - -/* XXX: Do we want this? - if (gstate->surface) - _cairo_surface_reference (gstate->surface); -*/ - - return gstate->surface; -} - -cairo_status_t -_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) -{ - if (pattern == NULL) - return CAIRO_STATUS_NULL_POINTER; - - cairo_pattern_reference (pattern); - cairo_pattern_destroy (gstate->pattern); - gstate->pattern = pattern; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_pattern_t * -_cairo_gstate_current_pattern (cairo_gstate_t *gstate) -{ - if (gstate == NULL) - return NULL; - -/* XXX: Do we want this? - cairo_pattern_reference (gstate->pattern); -*/ - - return gstate->pattern; -} - -cairo_status_t -_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator) -{ - gstate->operator = operator; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_operator_t -_cairo_gstate_current_operator (cairo_gstate_t *gstate) -{ - return gstate->operator; -} - -cairo_status_t -_cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, double blue) -{ - cairo_pattern_destroy (gstate->pattern); - - gstate->pattern = _cairo_pattern_create_solid (red, green, blue); - if (!gstate->pattern) - return CAIRO_STATUS_NO_MEMORY; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_current_rgb_color (cairo_gstate_t *gstate, double *red, double *green, double *blue) -{ - return _cairo_pattern_get_rgb (gstate->pattern, red, green, blue); -} - -cairo_status_t -_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) -{ - gstate->tolerance = tolerance; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_current_tolerance (cairo_gstate_t *gstate) -{ - return gstate->tolerance; -} - -cairo_status_t -_cairo_gstate_set_alpha (cairo_gstate_t *gstate, double alpha) -{ - gstate->alpha = alpha; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_current_alpha (cairo_gstate_t *gstate) -{ - return gstate->alpha; -} - -cairo_status_t -_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) -{ - gstate->fill_rule = fill_rule; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_fill_rule_t -_cairo_gstate_current_fill_rule (cairo_gstate_t *gstate) -{ - return gstate->fill_rule; -} - -cairo_status_t -_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) -{ - gstate->line_width = width; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_current_line_width (cairo_gstate_t *gstate) -{ - return gstate->line_width; -} - -cairo_status_t -_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) -{ - gstate->line_cap = line_cap; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_line_cap_t -_cairo_gstate_current_line_cap (cairo_gstate_t *gstate) -{ - return gstate->line_cap; -} - -cairo_status_t -_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join) -{ - gstate->line_join = line_join; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_line_join_t -_cairo_gstate_current_line_join (cairo_gstate_t *gstate) -{ - return gstate->line_join; -} - -cairo_status_t -_cairo_gstate_set_dash (cairo_gstate_t *gstate, double *dash, int num_dashes, double offset) -{ - if (gstate->dash) { - free (gstate->dash); - gstate->dash = NULL; - } - - gstate->num_dashes = num_dashes; - if (gstate->num_dashes) { - gstate->dash = malloc (gstate->num_dashes * sizeof (double)); - if (gstate->dash == NULL) { - gstate->num_dashes = 0; - return CAIRO_STATUS_NO_MEMORY; - } - } - - memcpy (gstate->dash, dash, gstate->num_dashes * sizeof (double)); - gstate->dash_offset = offset; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) -{ - gstate->miter_limit = limit; - - return CAIRO_STATUS_SUCCESS; -} - -double -_cairo_gstate_current_miter_limit (cairo_gstate_t *gstate) -{ - return gstate->miter_limit; -} - -void -_cairo_gstate_current_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) -{ - cairo_matrix_copy (matrix, &gstate->ctm); -} - -cairo_status_t -_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) -{ - cairo_matrix_t tmp; - - _cairo_gstate_unset_font (gstate); - - _cairo_matrix_set_translate (&tmp, tx, ty); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - _cairo_matrix_set_translate (&tmp, -tx, -ty); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) -{ - cairo_matrix_t tmp; - - if (sx == 0 || sy == 0) - return CAIRO_STATUS_INVALID_MATRIX; - - _cairo_gstate_unset_font (gstate); - - _cairo_matrix_set_scale (&tmp, sx, sy); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - _cairo_matrix_set_scale (&tmp, 1/sx, 1/sy); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) -{ - cairo_matrix_t tmp; - - _cairo_gstate_unset_font (gstate); - - _cairo_matrix_set_rotate (&tmp, angle); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - _cairo_matrix_set_rotate (&tmp, -angle); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_concat_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - cairo_matrix_t tmp; - - _cairo_gstate_unset_font (gstate); - - cairo_matrix_copy (&tmp, matrix); - cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); - - cairo_matrix_invert (&tmp); - cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_set_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - cairo_status_t status; - - _cairo_gstate_unset_font (gstate); - - cairo_matrix_copy (&gstate->ctm, matrix); - - cairo_matrix_copy (&gstate->ctm_inverse, matrix); - status = cairo_matrix_invert (&gstate->ctm_inverse); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_default_matrix (cairo_gstate_t *gstate) -{ - int scale = gstate->pixels_per_inch / CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT + 0.5; - if (scale == 0) - scale = 1; - - _cairo_gstate_unset_font (gstate); - - cairo_matrix_set_identity (&gstate->font_matrix); - - cairo_matrix_set_identity (&gstate->ctm); - cairo_matrix_scale (&gstate->ctm, scale, scale); - cairo_matrix_copy (&gstate->ctm_inverse, &gstate->ctm); - cairo_matrix_invert (&gstate->ctm_inverse); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_identity_matrix (cairo_gstate_t *gstate) -{ - _cairo_gstate_unset_font (gstate); - - cairo_matrix_set_identity (&gstate->ctm); - cairo_matrix_set_identity (&gstate->ctm_inverse); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_transform_point (cairo_gstate_t *gstate, double *x, double *y) -{ - cairo_matrix_transform_point (&gstate->ctm, x, y); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy) -{ - cairo_matrix_transform_distance (&gstate->ctm, dx, dy); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_inverse_transform_point (cairo_gstate_t *gstate, double *x, double *y) -{ - cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_inverse_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy) -{ - cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_new_path (cairo_gstate_t *gstate) -{ - _cairo_path_fini (&gstate->path); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y) -{ - cairo_point_t point; - - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); - - return _cairo_path_move_to (&gstate->path, &point); -} - -cairo_status_t -_cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y) -{ - cairo_point_t point; - - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); - - return _cairo_path_line_to (&gstate->path, &point); -} - -cairo_status_t -_cairo_gstate_curve_to (cairo_gstate_t *gstate, - double x0, double y0, - double x1, double y1, - double x2, double y2) -{ - cairo_point_t p0, p1, p2; - - cairo_matrix_transform_point (&gstate->ctm, &x0, &y0); - cairo_matrix_transform_point (&gstate->ctm, &x1, &y1); - cairo_matrix_transform_point (&gstate->ctm, &x2, &y2); - - p0.x = _cairo_fixed_from_double (x0); - p0.y = _cairo_fixed_from_double (y0); - - p1.x = _cairo_fixed_from_double (x1); - p1.y = _cairo_fixed_from_double (y1); - - p2.x = _cairo_fixed_from_double (x2); - p2.y = _cairo_fixed_from_double (y2); - - return _cairo_path_curve_to (&gstate->path, &p0, &p1, &p2); -} - -/* Spline deviation from the circle in radius would be given by: - - error = sqrt (x**2 + y**2) - 1 - - A simpler error function to work with is: - - e = x**2 + y**2 - 1 - - From "Good approximation of circles by curvature-continuous Bezier - curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric - Design 8 (1990) 22-41, we learn: - - abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) - - and - abs (error) =~ 1/2 * e - - Of course, this error value applies only for the particular spline - approximation that is used in _cairo_gstate_arc_segment. -*/ -static double -_arc_error_normalized (double angle) -{ - return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); -} - -static double -_arc_max_angle_for_tolerance_normalized (double tolerance) -{ - double angle, error; - int i; - - /* Use table lookup to reduce search time in most cases. */ - struct { - double angle; - double error; - } table[] = { - { M_PI / 1.0, 0.0185185185185185036127 }, - { M_PI / 2.0, 0.000272567143730179811158 }, - { M_PI / 3.0, 2.38647043651461047433e-05 }, - { M_PI / 4.0, 4.2455377443222443279e-06 }, - { M_PI / 5.0, 1.11281001494389081528e-06 }, - { M_PI / 6.0, 3.72662000942734705475e-07 }, - { M_PI / 7.0, 1.47783685574284411325e-07 }, - { M_PI / 8.0, 6.63240432022601149057e-08 }, - { M_PI / 9.0, 3.2715520137536980553e-08 }, - { M_PI / 10.0, 1.73863223499021216974e-08 }, - { M_PI / 11.0, 9.81410988043554039085e-09 }, - }; - int table_size = (sizeof (table) / sizeof (table[0])); - - for (i = 0; i < table_size; i++) - if (table[i].error < tolerance) - return table[i].angle; - - ++i; - do { - angle = M_PI / i++; - error = _arc_error_normalized (angle); - } while (error > tolerance); - - return angle; -} - -static int -_cairo_gstate_arc_segments_needed (cairo_gstate_t *gstate, - double angle, - double radius) -{ - double l1, l2, lmax; - double max_angle; - - _cairo_matrix_compute_eigen_values (&gstate->ctm, &l1, &l2); - - l1 = fabs (l1); - l2 = fabs (l2); - if (l1 > l2) - lmax = l1; - else - lmax = l2; - - max_angle = _arc_max_angle_for_tolerance_normalized (gstate->tolerance / (radius * lmax)); - - return (int) ceil (angle / max_angle); -} - -/* We want to draw a single spline approximating a circular arc radius - R from angle A to angle B. Since we want a symmetric spline that - matches the endpoints of the arc in position and slope, we know - that the spline control points must be: - - (R * cos(A), R * sin(A)) - (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) - (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) - (R * cos(B), R * sin(B)) - - for some value of h. - - "Approximation of circular arcs by cubic poynomials", Michael - Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides - various values of h along with error analysis for each. - - From that paper, a very practical value of h is: - - h = 4/3 * tan(angle/4) - - This value does not give the spline with minimal error, but it does - provide a very good approximation, (6th-order convergence), and the - error expression is quite simple, (see the comment for - _arc_error_normalized). -*/ -static cairo_status_t -_cairo_gstate_arc_segment (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle_A, double angle_B) -{ - cairo_status_t status; - double r_sin_A, r_cos_A; - double r_sin_B, r_cos_B; - double h; - - r_sin_A = radius * sin (angle_A); - r_cos_A = radius * cos (angle_A); - r_sin_B = radius * sin (angle_B); - r_cos_B = radius * cos (angle_B); - - h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); - - status = _cairo_gstate_curve_to (gstate, - xc + r_cos_A - h * r_sin_A, yc + r_sin_A + h * r_cos_A, - xc + r_cos_B + h * r_sin_B, yc + r_sin_B - h * r_cos_B, - xc + r_cos_B, yc + r_sin_B); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gstate_arc_dir (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle_min, - double angle_max, - cairo_direction_t dir) -{ - cairo_status_t status; - - while (angle_max - angle_min > 4 * M_PI) - angle_max -= 2 * M_PI; - - /* Recurse if drawing arc larger than pi */ - if (angle_max - angle_min > M_PI) { - double angle_mid = angle_min + (angle_max - angle_min) / 2.0; - /* XXX: Something tells me this block could be condensed. */ - if (dir == CAIRO_DIRECTION_FORWARD) { - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min, angle_mid, dir); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_mid, angle_max, dir); - if (status) - return status; - } else { - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_mid, angle_max, dir); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min, angle_mid, dir); - if (status) - return status; - } - } else { - int i, segments; - double angle, angle_step; - - segments = _cairo_gstate_arc_segments_needed (gstate, - angle_max - angle_min, - radius); - angle_step = (angle_max - angle_min) / (double) segments; - - if (dir == CAIRO_DIRECTION_FORWARD) { - angle = angle_min; - } else { - angle = angle_max; - angle_step = - angle_step; - } - - for (i = 0; i < segments; i++, angle += angle_step) { - _cairo_gstate_arc_segment (gstate, - xc, yc, - radius, - angle, - angle + angle_step); - } - - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_arc (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle1, double angle2) -{ - cairo_status_t status; - - if (radius <= 0.0) - return CAIRO_STATUS_SUCCESS; - - while (angle2 < angle1) - angle2 += 2 * M_PI; - - status = _cairo_gstate_line_to (gstate, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle1, angle2, CAIRO_DIRECTION_FORWARD); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_arc_negative (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle1, double angle2) -{ - cairo_status_t status; - - if (radius <= 0.0) - return CAIRO_STATUS_SUCCESS; - - while (angle2 > angle1) - angle2 -= 2 * M_PI; - - status = _cairo_gstate_line_to (gstate, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - if (status) - return status; - - status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle2, angle1, CAIRO_DIRECTION_REVERSE); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -/* XXX: NYI -cairo_status_t -_cairo_gstate_arc_to (cairo_gstate_t *gstate, - double x1, double y1, - double x2, double y2, - double radius) -{ - -} -*/ - -cairo_status_t -_cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy) -{ - cairo_distance_t distance; - - cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - - distance.dx = _cairo_fixed_from_double (dx); - distance.dy = _cairo_fixed_from_double (dy); - - return _cairo_path_rel_move_to (&gstate->path, &distance); -} - -cairo_status_t -_cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy) -{ - cairo_distance_t distance; - - cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - - distance.dx = _cairo_fixed_from_double (dx); - distance.dy = _cairo_fixed_from_double (dy); - - return _cairo_path_rel_line_to (&gstate->path, &distance); -} - -cairo_status_t -_cairo_gstate_rel_curve_to (cairo_gstate_t *gstate, - double dx0, double dy0, - double dx1, double dy1, - double dx2, double dy2) -{ - cairo_distance_t distance[3]; - - cairo_matrix_transform_distance (&gstate->ctm, &dx0, &dy0); - cairo_matrix_transform_distance (&gstate->ctm, &dx1, &dy1); - cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); - - distance[0].dx = _cairo_fixed_from_double (dx0); - distance[0].dy = _cairo_fixed_from_double (dy0); - - distance[1].dx = _cairo_fixed_from_double (dx1); - distance[1].dy = _cairo_fixed_from_double (dy1); - - distance[2].dx = _cairo_fixed_from_double (dx2); - distance[2].dy = _cairo_fixed_from_double (dy2); - - return _cairo_path_rel_curve_to (&gstate->path, - &distance[0], - &distance[1], - &distance[2]); -} - -/* XXX: NYI -cairo_status_t -_cairo_gstate_stroke_path (cairo_gstate_t *gstate) -{ - cairo_status_t status; - - _cairo_pen_init (&gstate); - return CAIRO_STATUS_SUCCESS; -} -*/ - -cairo_status_t -_cairo_gstate_close_path (cairo_gstate_t *gstate) -{ - return _cairo_path_close_path (&gstate->path); -} - -cairo_status_t -_cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_ret) -{ - cairo_status_t status; - cairo_point_t point; - double x, y; - - status = _cairo_path_current_point (&gstate->path, &point); - if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - x = 0.0; - y = 0.0; - } else { - x = _cairo_fixed_to_double (point.x); - y = _cairo_fixed_to_double (point.y); - cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); - } - - if (x_ret) - *x_ret = x; - if (y_ret) - *y_ret = y; - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct gstate_path_interpreter { - cairo_matrix_t ctm_inverse; - double tolerance; - cairo_point_t current_point; - - cairo_move_to_func_t *move_to; - cairo_line_to_func_t *line_to; - cairo_curve_to_func_t *curve_to; - cairo_close_path_func_t *close_path; - - void *closure; -} gpi_t; - -static cairo_status_t -_gpi_move_to (void *closure, cairo_point_t *point) -{ - gpi_t *gpi = closure; - double x, y; - - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); - - cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); - - gpi->move_to (gpi->closure, x, y); - gpi->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_gpi_line_to (void *closure, cairo_point_t *point) -{ - gpi_t *gpi = closure; - double x, y; - - x = _cairo_fixed_to_double (point->x); - y = _cairo_fixed_to_double (point->y); - - cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); - - gpi->line_to (gpi->closure, x, y); - gpi->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_gpi_curve_to (void *closure, - cairo_point_t *p1, - cairo_point_t *p2, - cairo_point_t *p3) -{ - gpi_t *gpi = closure; - cairo_status_t status; - cairo_spline_t spline; - double x1, y1, x2, y2, x3, y3; - - if (gpi->curve_to) { - x1 = _cairo_fixed_to_double (p1->x); - y1 = _cairo_fixed_to_double (p1->y); - cairo_matrix_transform_point (&gpi->ctm_inverse, &x1, &y1); - - x2 = _cairo_fixed_to_double (p2->x); - y2 = _cairo_fixed_to_double (p2->y); - cairo_matrix_transform_point (&gpi->ctm_inverse, &x2, &y2); - - x3 = _cairo_fixed_to_double (p3->x); - y3 = _cairo_fixed_to_double (p3->y); - cairo_matrix_transform_point (&gpi->ctm_inverse, &x3, &y3); - - gpi->curve_to (gpi->closure, x1, y1, x2, y2, x3, y3); - } else { - cairo_point_t *p0 = &gpi->current_point; - int i; - double x, y; - - status = _cairo_spline_init (&spline, p0, p1, p2, p3); - if (status == CAIRO_INT_STATUS_DEGENERATE) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_spline_decompose (&spline, gpi->tolerance); - if (status) - return status; - - for (i=1; i < spline.num_points; i++) { - x = _cairo_fixed_to_double (spline.points[i].x); - y = _cairo_fixed_to_double (spline.points[i].y); - - cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); - - gpi->line_to (gpi->closure, x, y); - } - } - - gpi->current_point = *p3; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_gpi_close_path (void *closure) -{ - gpi_t *gpi = closure; - - gpi->close_path (gpi->closure); - - gpi->current_point.x = 0; - gpi->current_point.y = 0; - - return CAIRO_STATUS_SUCCESS; -} - -/* It's OK for curve_path to be NULL. In that case, all curves in the - path will be decomposed into one or more calls to the line_to - function, (according to the current tolerance). */ -cairo_status_t -_cairo_gstate_interpret_path (cairo_gstate_t *gstate, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_curve_to_func_t *curve_to, - cairo_close_path_func_t *close_path, - void *closure) -{ - cairo_path_t path; - gpi_t gpi; - - /* Anything we want from gstate must be copied. We must not retain - pointers into gstate. */ - _cairo_path_init_copy (&path, &gstate->path); - - cairo_matrix_copy (&gpi.ctm_inverse, &gstate->ctm_inverse); - gpi.tolerance = gstate->tolerance; - - gpi.move_to = move_to; - gpi.line_to = line_to; - gpi.curve_to = curve_to; - gpi.close_path = close_path; - gpi.closure = closure; - - gpi.current_point.x = 0; - gpi.current_point.y = 0; - - return _cairo_path_interpret (&path, - CAIRO_DIRECTION_FORWARD, - _gpi_move_to, - _gpi_line_to, - _gpi_curve_to, - _gpi_close_path, - &gpi); -} - -/* XXX: gstate->alpha will be going away before too long, and when it - * does, it may make sense for this function to just disappear. - */ -static void -_cairo_gstate_pattern_init_copy (cairo_gstate_t *gstate, - cairo_pattern_union_t *pattern, - cairo_pattern_t *src) -{ - _cairo_pattern_init_copy (&pattern->base, src); - _cairo_pattern_transform (&pattern->base, &gstate->ctm_inverse); - _cairo_pattern_set_alpha (&pattern->base, gstate->alpha); -} - -cairo_status_t -_cairo_gstate_stroke (cairo_gstate_t *gstate) -{ - cairo_status_t status; - cairo_traps_t traps; - - if (gstate->line_width <= 0.0) - return CAIRO_STATUS_SUCCESS; - - _cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); - - _cairo_traps_init (&traps); - - status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); - if (status) { - _cairo_traps_fini (&traps); - return status; - } - - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->pattern, - gstate->operator, - gstate->surface, - &traps); - - _cairo_traps_fini (&traps); - - _cairo_gstate_new_path (gstate); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_in_stroke (cairo_gstate_t *gstate, - double x, - double y, - cairo_bool_t *inside_ret) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_traps_t traps; - - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - - _cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); - - _cairo_traps_init (&traps); - - status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); - if (status) - goto BAIL; - - *inside_ret = _cairo_traps_contain (&traps, x, y); - -BAIL: - _cairo_traps_fini (&traps); - - return status; -} - -/* XXX We currently have a confusing mix of boxes and rectangles as - * exemplified by this function. A cairo_box_t is a rectangular area - * represented by the coordinates of the upper left and lower right - * corners, expressed in fixed point numbers. A cairo_rectangle_t is - * also a rectangular area, but represented by the upper left corner - * and the width and the height, as integer numbers. - * - * This function converts a cairo_box_t to a cairo_rectangle_t by - * increasing the area to the nearest integer coordinates. We should - * standardize on cairo_rectangle_t and cairo_rectangle_fixed_t, and - * this function could be renamed to the more reasonable - * _cairo_rectangle_fixed_round. - */ - -static void -_cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle) -{ - rectangle->x = _cairo_fixed_integer_floor (box->p1.x); - rectangle->y = _cairo_fixed_integer_floor (box->p1.y); - rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; - rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; -} - -static void -_cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src) -{ - int x1, y1, x2, y2; - - x1 = MAX (dest->x, src->x); - y1 = MAX (dest->y, src->y); - x2 = MIN (dest->x + dest->width, src->x + src->width); - y2 = MIN (dest->y + dest->height, src->y + src->height); - - if (x1 >= x2 || y1 >= y2) { - dest->x = 0; - dest->y = 0; - dest->width = 0; - dest->height = 0; - } else { - dest->x = x1; - dest->y = y1; - dest->width = x2 - x1; - dest->height = y2 - y1; - } -} - -static int -_cairo_rectangle_empty (cairo_rectangle_t *rect) -{ - return rect->width == 0 || rect->height == 0; -} - -static void -translate_traps (cairo_traps_t *traps, int x, int y) -{ - cairo_fixed_t xoff, yoff; - cairo_trapezoid_t *t; - int i; - - /* Ugh. The cairo_composite/(Render) interface doesn't allow - an offset for the trapezoids. Need to manually shift all - the coordinates to align with the offset origin of the - intermediate surface. */ - - xoff = _cairo_fixed_from_int (x); - yoff = _cairo_fixed_from_int (y); - - for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { - t->top += yoff; - t->bottom += yoff; - t->left.p1.x += xoff; - t->left.p1.y += yoff; - t->left.p2.x += xoff; - t->left.p2.y += yoff; - t->right.p1.x += xoff; - t->right.p1.y += yoff; - t->right.p2.x += xoff; - t->right.p2.y += yoff; - } -} - - -/* Warning: This call modifies the coordinates of traps */ -static cairo_status_t -_cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_pattern_t *src, - cairo_operator_t operator, - cairo_surface_t *dst, - cairo_traps_t *traps) -{ - cairo_status_t status; - cairo_pattern_union_t pattern; - cairo_rectangle_t extents; - cairo_box_t trap_extents; - - if (traps->num_traps == 0) - return CAIRO_STATUS_SUCCESS; - - if (gstate->surface == NULL) - return CAIRO_STATUS_NO_TARGET_SURFACE; - - _cairo_traps_extents (traps, &trap_extents); - _cairo_box_round_to_rectangle (&trap_extents, &extents); - - if (gstate->clip.surface) { - cairo_surface_t *intermediate; - cairo_surface_pattern_t intermediate_pattern; - cairo_color_t empty_color; - - _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - - if (_cairo_rectangle_empty (&extents)) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL1; - } - - translate_traps (traps, -extents.x, -extents.y); - - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, 0.); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - extents.width, - extents.height, - &empty_color); - if (intermediate == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } - - _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); - - status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - &pattern.base, - intermediate, - extents.x, extents.y, - 0, 0, - extents.width, - extents.height, - traps->traps, - traps->num_traps); - _cairo_pattern_fini (&pattern.base); - - if (status) - goto BAIL2; - - - _cairo_pattern_init_for_surface (&pattern.surface, - gstate->clip.surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - intermediate, - extents.x - gstate->clip.rect.x, - extents.y - gstate->clip.rect.y, - 0, 0, - 0, 0, - extents.width, extents.height); - _cairo_pattern_fini (&pattern.base); - - if (status) - goto BAIL2; - - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - _cairo_gstate_pattern_init_copy (gstate, &pattern, src); - - status = _cairo_surface_composite (operator, - &pattern.base, - &intermediate_pattern.base, - dst, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - - _cairo_pattern_fini (&pattern.base); - _cairo_pattern_fini (&intermediate_pattern.base); - - BAIL2: - cairo_surface_destroy (intermediate); - BAIL1: - - if (status) - return status; - - } else { - if (gstate->clip.region) { - pixman_box16_t box; - pixman_box16_t *intersection_extents; - pixman_region16_t *rect, *intersection; - - box.x1 = _cairo_fixed_integer_floor (trap_extents.p1.x); - box.y1 = _cairo_fixed_integer_floor (trap_extents.p1.y); - box.x2 = _cairo_fixed_integer_ceil (trap_extents.p2.x); - box.y2 = _cairo_fixed_integer_ceil (trap_extents.p2.y); - - rect = pixman_region_create_simple (&box); - if (rect == NULL) - goto bail1; - intersection = pixman_region_create(); - if (intersection == NULL) - goto bail2; - - if (pixman_region_intersect (intersection, gstate->clip.region, - rect) != PIXMAN_REGION_STATUS_SUCCESS) - goto bail3; - intersection_extents = pixman_region_extents (intersection); - - extents.x = intersection_extents->x1; - extents.y = intersection_extents->y1; - extents.width = intersection_extents->x2 - intersection_extents->x1; - extents.height = intersection_extents->y2 - intersection_extents->y1; - bail3: - pixman_region_destroy (intersection); - bail2: - pixman_region_destroy (rect); - bail1: - ; - } - - _cairo_gstate_pattern_init_copy (gstate, &pattern, src); - - status = _cairo_surface_composite_trapezoids (gstate->operator, - &pattern.base, dst, - extents.x, extents.y, - extents.x, extents.y, - extents.width, - extents.height, - traps->traps, - traps->num_traps); - - _cairo_pattern_fini (&pattern.base); - - if (status) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_fill (cairo_gstate_t *gstate) -{ - cairo_status_t status; - cairo_traps_t traps; - - _cairo_traps_init (&traps); - - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); - if (status) { - _cairo_traps_fini (&traps); - return status; - } - - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->pattern, - gstate->operator, - gstate->surface, - &traps); - - _cairo_traps_fini (&traps); - - _cairo_gstate_new_path (gstate); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_in_fill (cairo_gstate_t *gstate, - double x, - double y, - cairo_bool_t *inside_ret) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_traps_t traps; - - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - - _cairo_traps_init (&traps); - - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); - if (status) - goto BAIL; - - *inside_ret = _cairo_traps_contain (&traps, x, y); - -BAIL: - _cairo_traps_fini (&traps); - - return status; -} - -cairo_status_t -_cairo_gstate_copy_page (cairo_gstate_t *gstate) -{ - if (gstate->surface == NULL) - return CAIRO_STATUS_NO_TARGET_SURFACE; - - return _cairo_surface_copy_page (gstate->surface); -} - -cairo_status_t -_cairo_gstate_show_page (cairo_gstate_t *gstate) -{ - if (gstate->surface == NULL) - return CAIRO_STATUS_NO_TARGET_SURFACE; - - return _cairo_surface_show_page (gstate->surface); -} - -cairo_status_t -_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, - double *x1, double *y1, - double *x2, double *y2) -{ - cairo_status_t status; - cairo_traps_t traps; - cairo_box_t extents; - - _cairo_traps_init (&traps); - - status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); - if (status) - goto BAIL; - - _cairo_traps_extents (&traps, &extents); - - *x1 = _cairo_fixed_to_double (extents.p1.x); - *y1 = _cairo_fixed_to_double (extents.p1.y); - *x2 = _cairo_fixed_to_double (extents.p2.x); - *y2 = _cairo_fixed_to_double (extents.p2.y); - - cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); - cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); - -BAIL: - _cairo_traps_fini (&traps); - - return status; -} - -cairo_status_t -_cairo_gstate_fill_extents (cairo_gstate_t *gstate, - double *x1, double *y1, - double *x2, double *y2) -{ - cairo_status_t status; - cairo_traps_t traps; - cairo_box_t extents; - - _cairo_traps_init (&traps); - - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); - if (status) - goto BAIL; - - _cairo_traps_extents (&traps, &extents); - - *x1 = _cairo_fixed_to_double (extents.p1.x); - *y1 = _cairo_fixed_to_double (extents.p1.y); - *x2 = _cairo_fixed_to_double (extents.p2.x); - *y2 = _cairo_fixed_to_double (extents.p2.y); - - cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); - cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); - -BAIL: - _cairo_traps_fini (&traps); - - return status; -} - -cairo_status_t -_cairo_gstate_init_clip (cairo_gstate_t *gstate) -{ - /* destroy any existing clip-region artifacts */ - if (gstate->clip.surface) - cairo_surface_destroy (gstate->clip.surface); - gstate->clip.surface = NULL; - - if (gstate->clip.region) - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = NULL; - - /* reset the surface's clip to the whole surface */ - if (gstate->surface) - _cairo_surface_set_clip_region (gstate->surface, - gstate->clip.region); - - return CAIRO_STATUS_SUCCESS; -} - -static int -extract_transformed_rectangle(cairo_matrix_t *mat, - cairo_traps_t *tr, - pixman_box16_t *box) -{ - double a, b, c, d, tx, ty; - cairo_status_t st; - - st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); - if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) - return 0; - - if (tr->num_traps == 1 - && tr->traps[0].left.p1.x == tr->traps[0].left.p2.x - && tr->traps[0].right.p1.x == tr->traps[0].right.p2.x - && tr->traps[0].left.p1.y == tr->traps[0].right.p1.y - && tr->traps[0].left.p2.y == tr->traps[0].right.p2.y - && _cairo_fixed_is_integer(tr->traps[0].left.p1.x) - && _cairo_fixed_is_integer(tr->traps[0].left.p1.y) - && _cairo_fixed_is_integer(tr->traps[0].left.p2.x) - && _cairo_fixed_is_integer(tr->traps[0].left.p2.y) - && _cairo_fixed_is_integer(tr->traps[0].right.p1.x) - && _cairo_fixed_is_integer(tr->traps[0].right.p1.y) - && _cairo_fixed_is_integer(tr->traps[0].right.p2.x) - && _cairo_fixed_is_integer(tr->traps[0].right.p2.y)) { - - box->x1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.x); - box->x2 = (short) _cairo_fixed_integer_part(tr->traps[0].right.p1.x); - box->y1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.y); - box->y2 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p2.y); - return 1; - } - return 0; -} - -/* Reset surface clip region to the one in the gstate */ -cairo_status_t -_cairo_gstate_restore_external_state (cairo_gstate_t *gstate) -{ - cairo_status_t status; - - status = CAIRO_STATUS_SUCCESS; - - if (gstate->surface) - status = _cairo_surface_set_clip_region (gstate->surface, - gstate->clip.region); - - /* If not supported we're already using surface clipping */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - status = CAIRO_STATUS_SUCCESS; - - return status; -} - -cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate) -{ - cairo_status_t status; - cairo_pattern_union_t pattern; - cairo_traps_t traps; - cairo_color_t white_color; - cairo_box_t extents; - pixman_box16_t box; - - /* Fill the clip region as traps. */ - - _cairo_traps_init (&traps); - status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); - if (status) { - _cairo_traps_fini (&traps); - return status; - } - - /* Check to see if we can represent these traps as a PixRegion. */ - - if (extract_transformed_rectangle (&gstate->ctm, &traps, &box)) { - - pixman_region16_t *rect = NULL; - pixman_region16_t *intersection = NULL; - - status = CAIRO_STATUS_SUCCESS; - rect = pixman_region_create_simple (&box); - - if (rect == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - - } else { - - if (gstate->clip.region == NULL) { - gstate->clip.region = rect; - } else { - intersection = pixman_region_create(); - if (pixman_region_intersect (intersection, - gstate->clip.region, rect) - == PIXMAN_REGION_STATUS_SUCCESS) { - pixman_region_destroy (gstate->clip.region); - gstate->clip.region = intersection; - } else { - status = CAIRO_STATUS_NO_MEMORY; - } - pixman_region_destroy (rect); - } - - if (!status) - status = _cairo_surface_set_clip_region (gstate->surface, - gstate->clip.region); - } - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - _cairo_traps_fini (&traps); - return status; - } - - /* Fall through as status == CAIRO_INT_STATUS_UNSUPPORTED - means that backend doesn't support clipping regions and - mask surface clipping should be used instead. */ - } - - /* Otherwise represent the clip as a mask surface. */ - - _cairo_color_init (&white_color); - - if (gstate->clip.surface == NULL) { - _cairo_traps_extents (&traps, &extents); - _cairo_box_round_to_rectangle (&extents, &gstate->clip.rect); - gstate->clip.surface = - _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_A8, - gstate->clip.rect.width, - gstate->clip.rect.height, - &white_color); - if (gstate->clip.surface == NULL) - return CAIRO_STATUS_NO_MEMORY; - } - - translate_traps (&traps, -gstate->clip.rect.x, -gstate->clip.rect.y); - _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); - - status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN, - &pattern.base, - gstate->clip.surface, - 0, 0, - 0, 0, - gstate->clip.rect.width, - gstate->clip.rect.height, - traps.traps, - traps.num_traps); - - _cairo_pattern_fini (&pattern.base); - - _cairo_traps_fini (&traps); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_show_surface (cairo_gstate_t *gstate, - cairo_surface_t *surface, - int width, - int height) -{ - - /* We are dealing with 6 coordinate spaces in this function. this makes - * it ugly. - * - * - "Image" space is the space of the surface we're reading pixels from. - * it is the surface argument to this function. The surface has a - * matrix attached to it which maps "user" space (see below) into - * image space. - * - * - "Device" space is the space of the surface we're ultimately writing - * pixels to. It is the current surface of the gstate argument to - * this function. - * - * - "User" space is an arbitrary space defined by the user, defined - * implicitly by the gstate's CTM. The CTM maps from user space to - * device space. The CTM inverse (which is also kept at all times) - * maps from device space to user space. - * - * - "Clip" space is the space of the surface being used to clip pixels - * during compositing. Space-wise, it is a bounding box (offset+size) - * within device space. This surface is usually smaller than the device - * surface (and possibly the image surface too) and logically occupies - * a bounding box around the "clip path", situated somewhere in device - * space. The clip path is already painted on the clip surface. - * - * - "Intermediate" space is the subset of the Clip space that the - * drawing will affect, and we allocate an intermediate surface - * of this size so that we can paint in it. - * - * - "Pattern" space is another arbitrary space defined in the pattern - * element of gstate. As pixels are read from image space, they are - * combined with pixels being read from pattern space and pixels - * already existing in device space. User coordinates are converted - * to pattern space, similarly, using a matrix attached to the pattern. - * (in fact, there is a 7th space in here, which is the space of the - * surface acting as a source for the pattern) - * - * To composite these spaces, we temporarily change the image surface - * so that it can be read and written in device coordinates; in a sense - * this makes it "spatially compatible" with the clip and device spaces. - * - * - * There is also some confusion about the interaction between a clip and - * a pattern; it is assumed that in this "show surface" operation a pattern - * is to be used as an auxiliary alpha mask. this might be wrong, but it's - * what we're doing now. - * - * so, to follow the operations below, remember that in the compositing - * model, each operation is always of the form ((src IN mask) OP dst). - * that's the basic operation. - * - * so the compositing we are trying to do here, in general, involves 2 - * steps, going via a temporary surface: - * - * - combining clip and pattern pixels together into a mask channel. - * this will be ((pattern IN clip) SRC temporary). it ignores the - * pixels already in the temporary, overwriting it with the - * pattern, clipped to the clip mask. - * - * - combining temporary and "image" pixels with "device" pixels, - * with a user-provided porter/duff operator. this will be - * ((image IN temporary) OP device). - * - * if there is no clip, the degenerate case is just the second step - * with pattern standing in for temporary. - * - */ - - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_matrix_t image_to_user, image_to_device; - double device_x, device_y; - double device_width, device_height; - cairo_surface_pattern_t pattern; - cairo_box_t pattern_extents; - cairo_rectangle_t extents; - - cairo_surface_get_matrix (surface, &image_to_user); - cairo_matrix_invert (&image_to_user); - cairo_matrix_multiply (&image_to_device, &image_to_user, &gstate->ctm); - - _cairo_gstate_current_point (gstate, &device_x, &device_y); - device_width = width; - device_height = height; - _cairo_matrix_transform_bounding_box (&image_to_device, - &device_x, &device_y, - &device_width, &device_height); - - _cairo_pattern_init_for_surface (&pattern, surface); - - /* inherit surface attributes while surface attribute functions still - exist */ - pattern.base.matrix = surface->matrix; - pattern.base.filter = surface->filter; - if (surface->repeat) - pattern.base.extend = CAIRO_EXTEND_REPEAT; - else - pattern.base.extend = CAIRO_EXTEND_NONE; - - _cairo_pattern_transform (&pattern.base, &gstate->ctm_inverse); - _cairo_pattern_set_alpha (&pattern.base, gstate->alpha); - - pattern_extents.p1.x = _cairo_fixed_from_double (device_x); - pattern_extents.p1.y = _cairo_fixed_from_double (device_y); - pattern_extents.p2.x = _cairo_fixed_from_double (device_x + device_width); - pattern_extents.p2.y = _cairo_fixed_from_double (device_y + device_height); - _cairo_box_round_to_rectangle (&pattern_extents, &extents); - - if (gstate->clip.surface) - { - _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - - /* We only need to composite if the rectangle is not empty. */ - if (!_cairo_rectangle_empty (&extents)) { - cairo_surface_pattern_t clip_pattern; - - _cairo_pattern_init_for_surface (&clip_pattern, - gstate->clip.surface); - - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - &clip_pattern.base, - gstate->surface, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - - _cairo_pattern_fini (&clip_pattern.base); - } - } - else - { - /* XXX: The rendered size is sometimes 1 or 2 pixels short - * from what I expect. Need to fix this. - * KRH: I'm guessing this was due to rounding error when - * passing double coordinates for integer arguments. Using - * the extents rectangle should fix this, since it's properly - * rounded. Is this still the case? - */ - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - NULL, - gstate->surface, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - - } - - _cairo_pattern_fini (&pattern.base); - - return status; -} - -static void -_cairo_gstate_unset_font (cairo_gstate_t *gstate) -{ - if (gstate->font) { - cairo_font_destroy (gstate->font); - gstate->font = NULL; - } -} - -cairo_status_t -_cairo_gstate_select_font (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - char *new_family; - - new_family = strdup (family); - if (!new_family) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_gstate_unset_font (gstate); - - gstate->font_family = new_family; - gstate->font_slant = slant; - gstate->font_weight = weight; - - cairo_matrix_set_identity (&gstate->font_matrix); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_scale_font (cairo_gstate_t *gstate, - double scale) -{ - _cairo_gstate_unset_font (gstate); - - return cairo_matrix_scale (&gstate->font_matrix, scale, scale); -} - -cairo_status_t -_cairo_gstate_transform_font (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - cairo_matrix_t tmp; - double a, b, c, d, tx, ty; - - _cairo_gstate_unset_font (gstate); - - cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); - cairo_matrix_set_affine (&tmp, a, b, c, d, 0, 0); - return cairo_matrix_multiply (&gstate->font_matrix, &gstate->font_matrix, &tmp); -} - - -cairo_status_t -_cairo_gstate_current_font (cairo_gstate_t *gstate, - cairo_font_t **font) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_font (gstate); - if (status) - return status; - - *font = gstate->font; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gstate_set_font_transform (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - _cairo_gstate_unset_font (gstate); - - cairo_matrix_copy (&gstate->font_matrix, matrix); -} - -void -_cairo_gstate_current_font_transform (cairo_gstate_t *gstate, - cairo_matrix_t *matrix) -{ - cairo_matrix_copy (matrix, &gstate->font_matrix); -} - -/* - * Like everything else in this file, fonts involve Too Many Coordinate Spaces; - * it is easy to get confused about what's going on. - * - * The user's view - * --------------- - * - * Users ask for things in user space. When cairo starts, a user space unit - * is about 1/96 inch, which is similar to (but importantly different from) - * the normal "point" units most users think in terms of. When a user - * selects a font, its scale is set to "one user unit". The user can then - * independently scale the user coordinate system *or* the font matrix, in - * order to adjust the rendered size of the font. - * - * The only font type exposed to the user is cairo_font_t which is a - * a font specialized to a particular scale matrix, CTM, and target - * surface. The user is responsible for not using a cairo_font_t - * after changing the parameters; doing so will produce garbled metrics. - * - * - * The font's view - * --------------- - * - * Fonts are designed and stored (in say .ttf files) in "font space", which - * describes an "EM Square" (a design tile) and has some abstract number - * such as 1000, 1024, or 2048 units per "EM". This is basically an - * uninteresting space for us, but we need to remember that it exists. - * - * Font resources (from libraries or operating systems) render themselves - * to a particular device. Since they do not want to make most programmers - * worry about the font design space, the scaling API is simplified to - * involve just telling the font the required pixel size of the EM square - * (that is, in device space). - * - * - * Cairo's gstate view - * ------------------- - * - * In addition to the CTM and CTM inverse, we keep a matrix in the gstate - * called the "font matrix" which describes the user's most recent - * font-scaling or font-transforming request. This is kept in terms of an - * abstract scale factor, composed with the CTM and used to set the font's - * pixel size. So if the user asks to "scale the font by 12", the matrix - * is: - * - * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] - * - * It is an affine matrix, like all cairo matrices, but its tx and ty - * components are always set to zero; we don't permit "nudging" fonts - * around. - * - * In order to perform any action on a font, we must build an object - * called a cairo_font_scale_t; this contains the central 2x2 matrix - * resulting from "font matrix * CTM". - * - * We pass this to the font when making requests of it, which causes it to - * reply for a particular [user request, device] combination, under the CTM - * (to accomodate the "zoom in" == "bigger fonts" issue above). - * - * The other terms in our communication with the font are therefore in - * device space. When we ask it to perform text->glyph conversion, it will - * produce a glyph string in device space. Glyph vectors we pass to it for - * measuring or rendering should be in device space. The metrics which we - * get back from the font will be in device space. The contents of the - * global glyph image cache will be in device space. - * - * - * Cairo's public view - * ------------------- - * - * Since the values entering and leaving via public API calls are in user - * space, the gstate functions typically need to multiply argumens by the - * CTM (for user-input glyph vectors), and return values by the CTM inverse - * (for font responses such as metrics or glyph vectors). - * - */ - -void -_cairo_gstate_current_font_scale (cairo_gstate_t *gstate, - cairo_font_scale_t *sc) -{ - cairo_matrix_t tmp; - double dummy; - cairo_matrix_multiply (&tmp, &gstate->font_matrix, &gstate->ctm); - cairo_matrix_get_affine (&tmp, - &sc->matrix[0][0], - &sc->matrix[0][1], - &sc->matrix[1][0], - &sc->matrix[1][1], - &dummy, &dummy); -} - -static cairo_status_t -_cairo_gstate_ensure_font (cairo_gstate_t *gstate) -{ - cairo_font_scale_t sc; - cairo_status_t status; - const char *family; - - if (gstate->font) - return CAIRO_STATUS_SUCCESS; - - _cairo_gstate_current_font_scale (gstate, &sc); - - if (gstate->font_family) - family = gstate->font_family; - else - family = CAIRO_FONT_FAMILY_DEFAULT; - - status = _cairo_font_create (family, - gstate->font_slant, - gstate->font_weight, - &sc, - &gstate->font); - - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) -{ - cairo_status_t status = _cairo_gstate_ensure_font (gstate); - if (status) - return status; - - return cairo_font_extents (gstate->font, - &gstate->font_matrix, - extents); -} - -cairo_status_t -_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *nglyphs) -{ - cairo_status_t status; - - cairo_point_t point; - double origin_x, origin_y; - int i; - - status = _cairo_gstate_ensure_font (gstate); - if (status) - return status; - - status = _cairo_path_current_point (&gstate->path, &point); - if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - origin_x = 0.0; - origin_y = 0.0; - } else { - origin_x = _cairo_fixed_to_double (point.x); - origin_y = _cairo_fixed_to_double (point.y); - cairo_matrix_transform_point (&gstate->ctm_inverse, - &origin_x, &origin_y); - } - - status = _cairo_font_text_to_glyphs (gstate->font, - utf8, glyphs, nglyphs); - - if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs)) - return status; - - /* The font responded in glyph space, starting from (0,0). Convert to - user space by applying the font transform, then add any current point - offset. */ - - for (i = 0; i < *nglyphs; ++i) { - cairo_matrix_transform_point (&gstate->font_matrix, - &((*glyphs)[i].x), - &((*glyphs)[i].y)); - (*glyphs)[i].x += origin_x; - (*glyphs)[i].y += origin_y; - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font) -{ - if (font != gstate->font) { - if (gstate->font) - cairo_font_destroy (gstate->font); - gstate->font = font; - if (gstate->font) - cairo_font_reference (gstate->font); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_font (gstate); - if (status) - return status; - - cairo_font_glyph_extents (gstate->font, - &gstate->font_matrix, - glyphs, num_glyphs, - extents); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gstate_show_glyphs (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_status_t status; - int i; - cairo_glyph_t *transformed_glyphs = NULL; - cairo_pattern_union_t pattern; - cairo_box_t bbox; - cairo_rectangle_t extents; - - status = _cairo_gstate_ensure_font (gstate); - if (status) - return status; - - transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); - if (transformed_glyphs == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < num_glyphs; ++i) - { - transformed_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&gstate->ctm, - &transformed_glyphs[i].x, - &transformed_glyphs[i].y); - } - - status = _cairo_font_glyph_bbox (gstate->font, - transformed_glyphs, num_glyphs, - &bbox); - _cairo_box_round_to_rectangle (&bbox, &extents); - - if (status) - goto CLEANUP_GLYPHS; - - if (gstate->clip.surface) - { - cairo_surface_t *intermediate; - cairo_surface_pattern_t intermediate_pattern; - cairo_color_t empty_color; - - _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - - /* Shortcut if empty */ - if (_cairo_rectangle_empty (&extents)) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL1; - } - - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, .0); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - extents.width, - extents.height, - &empty_color); - if (intermediate == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } - - /* move the glyphs again, from dev space to intermediate space */ - for (i = 0; i < num_glyphs; ++i) - { - transformed_glyphs[i].x -= extents.x; - transformed_glyphs[i].y -= extents.y; - } - - _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); - - status = _cairo_font_show_glyphs (gstate->font, - CAIRO_OPERATOR_ADD, - &pattern.base, intermediate, - extents.x, extents.y, - 0, 0, - extents.width, extents.height, - transformed_glyphs, num_glyphs); - - _cairo_pattern_fini (&pattern.base); - - if (status) - goto BAIL2; - - _cairo_pattern_init_for_surface (&pattern.surface, - gstate->clip.surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - intermediate, - extents.x - gstate->clip.rect.x, - extents.y - gstate->clip.rect.y, - 0, 0, - 0, 0, - extents.width, extents.height); - - _cairo_pattern_fini (&pattern.base); - - if (status) - goto BAIL2; - - _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); - _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); - - status = _cairo_surface_composite (gstate->operator, - &pattern.base, - &intermediate_pattern.base, - gstate->surface, - extents.x, extents.y, - 0, 0, - extents.x, extents.y, - extents.width, extents.height); - _cairo_pattern_fini (&pattern.base); - _cairo_pattern_fini (&intermediate_pattern.base); - - BAIL2: - cairo_surface_destroy (intermediate); - BAIL1: - ; - } - else - { - _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); - - status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, &pattern.base, - gstate->surface, - extents.x, extents.y, - extents.x, extents.y, - extents.width, extents.height, - transformed_glyphs, num_glyphs); - - _cairo_pattern_fini (&pattern.base); - } - - CLEANUP_GLYPHS: - free (transformed_glyphs); - - return status; -} - -cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_status_t status; - int i; - cairo_glyph_t *transformed_glyphs = NULL; - - transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); - if (transformed_glyphs == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < num_glyphs; ++i) - { - transformed_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&gstate->ctm, - &(transformed_glyphs[i].x), - &(transformed_glyphs[i].y)); - } - - status = _cairo_font_glyph_path (gstate->font, - transformed_glyphs, num_glyphs, - &gstate->path); - - free (transformed_glyphs); - return status; -} diff --git a/src/cairo_hull.c b/src/cairo_hull.c deleted file mode 100644 index c93d70625..000000000 --- a/src/cairo_hull.c +++ /dev/null @@ -1,202 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2003 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -typedef struct cairo_hull -{ - cairo_point_t point; - cairo_slope_t slope; - int discard; -} cairo_hull_t; - -static cairo_hull_t * -_cairo_hull_create (cairo_pen_vertex_t *vertices, int num_vertices) -{ - int i; - cairo_hull_t *hull; - cairo_point_t *p, *extremum, tmp; - - extremum = &vertices[0].point; - for (i = 1; i < num_vertices; i++) { - p = &vertices[i].point; - if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x)) - extremum = p; - } - /* Put the extremal point at the beginning of the array */ - tmp = *extremum; - *extremum = vertices[0].point; - vertices[0].point = tmp; - - hull = malloc (num_vertices * sizeof (cairo_hull_t)); - if (hull == NULL) - return NULL; - - for (i = 0; i < num_vertices; i++) { - hull[i].point = vertices[i].point; - _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point); - - /* Discard all points coincident with the extremal point */ - if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0) - hull[i].discard = 1; - else - hull[i].discard = 0; - } - - return hull; -} - -static int -_cairo_hull_vertex_compare (const void *av, const void *bv) -{ - cairo_hull_t *a = (cairo_hull_t *) av; - cairo_hull_t *b = (cairo_hull_t *) bv; - int ret; - - ret = _cairo_slope_compare (&a->slope, &b->slope); - - /* In the case of two vertices with identical slope from the - extremal point discard the nearer point. */ - - if (ret == 0) { - cairo_fixed_48_16_t a_dist, b_dist; - a_dist = ((cairo_fixed_48_16_t) a->slope.dx * a->slope.dx + - (cairo_fixed_48_16_t) a->slope.dy * a->slope.dy); - b_dist = ((cairo_fixed_48_16_t) b->slope.dx * b->slope.dx + - (cairo_fixed_48_16_t) b->slope.dy * b->slope.dy); - if (a_dist < b_dist) { - a->discard = 1; - ret = -1; - } else { - b->discard = 1; - ret = 1; - } - } - - return ret; -} - -static int -_cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index) -{ - do { - /* hull[0] is always valid, so don't test and wraparound */ - index--; - } while (hull[index].discard); - - return index; -} - -static int -_cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index) -{ - do { - index = (index + 1) % num_hull; - } while (hull[index].discard); - - return index; -} - -static cairo_status_t -_cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull) -{ - int i, j, k; - cairo_slope_t slope_ij, slope_jk; - - i = 0; - j = _cairo_hull_next_valid (hull, num_hull, i); - k = _cairo_hull_next_valid (hull, num_hull, j); - - do { - _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point); - _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point); - - /* Is the angle formed by ij and jk concave? */ - if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) { - if (i == k) - return CAIRO_STATUS_SUCCESS; - hull[j].discard = 1; - j = i; - i = _cairo_hull_prev_valid (hull, num_hull, j); - } else { - i = j; - j = k; - k = _cairo_hull_next_valid (hull, num_hull, j); - } - } while (j != 0); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices) -{ - int i, j = 0; - - for (i = 0; i < *num_vertices; i++) { - if (hull[i].discard) - continue; - vertices[j++].point = hull[i].point; - } - - *num_vertices = j; - - return CAIRO_STATUS_SUCCESS; -} - -/* Given a set of vertices, compute the convex hull using the Graham - scan algorithm. */ -cairo_status_t -_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) -{ - cairo_hull_t *hull; - int num_hull = *num_vertices; - - hull = _cairo_hull_create (vertices, num_hull); - if (hull == NULL) - return CAIRO_STATUS_NO_MEMORY; - - qsort (hull + 1, num_hull - 1, - sizeof (cairo_hull_t), _cairo_hull_vertex_compare); - - _cairo_hull_eliminate_concave (hull, num_hull); - - _cairo_hull_to_pen (hull, vertices, num_vertices); - - free (hull); - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo_image_surface.c b/src/cairo_image_surface.c deleted file mode 100644 index 9745b3150..000000000 --- a/src/cairo_image_surface.c +++ /dev/null @@ -1,675 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2003 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -static const cairo_surface_backend_t cairo_image_surface_backend; - -static int -_cairo_format_bpp (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_A1: - return 1; - case CAIRO_FORMAT_A8: - return 8; - case CAIRO_FORMAT_RGB24: - case CAIRO_FORMAT_ARGB32: - default: - return 32; - } -} - -static cairo_image_surface_t * -_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, - cairo_format_t format) -{ - cairo_image_surface_t *surface; - - surface = malloc (sizeof (cairo_image_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init (&surface->base, &cairo_image_surface_backend); - - surface->pixman_image = pixman_image; - - surface->format = format; - surface->data = (char *) pixman_image_get_data (pixman_image); - surface->owns_data = 0; - - surface->width = pixman_image_get_width (pixman_image); - surface->height = pixman_image_get_height (pixman_image); - surface->stride = pixman_image_get_stride (pixman_image); - surface->depth = pixman_image_get_depth (pixman_image); - - return surface; -} - -cairo_image_surface_t * -_cairo_image_surface_create_with_masks (char *data, - cairo_format_masks_t *format, - int width, - int height, - int stride) -{ - cairo_image_surface_t *surface; - pixman_format_t *pixman_format; - pixman_image_t *pixman_image; - - pixman_format = pixman_format_create_masks (format->bpp, - format->alpha_mask, - format->red_mask, - format->green_mask, - format->blue_mask); - - if (pixman_format == NULL) - return NULL; - - pixman_image = pixman_image_create_for_data ((pixman_bits_t *) data, pixman_format, - width, height, format->bpp, stride); - - pixman_format_destroy (pixman_format); - - if (pixman_image == NULL) - return NULL; - - surface = _cairo_image_surface_create_for_pixman_image (pixman_image, - (cairo_format_t)-1); - - return surface; -} - -static pixman_format_t * -_create_pixman_format (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_A1: - return pixman_format_create (PIXMAN_FORMAT_NAME_A1); - break; - case CAIRO_FORMAT_A8: - return pixman_format_create (PIXMAN_FORMAT_NAME_A8); - break; - case CAIRO_FORMAT_RGB24: - return pixman_format_create (PIXMAN_FORMAT_NAME_RGB24); - break; - case CAIRO_FORMAT_ARGB32: - default: - return pixman_format_create (PIXMAN_FORMAT_NAME_ARGB32); - break; - } -} - -/** - * cairo_image_surface_create: - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates an image surface of the specified format and - * dimensions. The initial contents of the surface is undefined; you - * must explicitely clear the buffer, using, for example, - * cairo_rectangle() and cairo_fill() if you want it cleared. - * - * Return value: the newly created surface, or %NULL if it couldn't - * be created because of lack of memory - **/ -cairo_surface_t * -cairo_image_surface_create (cairo_format_t format, - int width, - int height) -{ - cairo_image_surface_t *surface; - pixman_format_t *pixman_format; - pixman_image_t *pixman_image; - - pixman_format = _create_pixman_format (format); - if (pixman_format == NULL) - return NULL; - - pixman_image = pixman_image_create (pixman_format, width, height); - - pixman_format_destroy (pixman_format); - - if (pixman_image == NULL) - return NULL; - - surface = _cairo_image_surface_create_for_pixman_image (pixman_image, format); - - return &surface->base; -} - -/** - * cairo_image_surface_create_for_data: - * @data: a pointer to a buffer supplied by the application - * in which to write contents. - * @format: the format of pixels in the buffer - * @width: the width of the image to be stored in the buffer - * @height: the height of the image to be stored in the buffer - * @stride: the number of bytes between the start of rows - * in the buffer. Having this be specified separate from @width - * allows for padding at the end of rows, or for writing - * to a subportion of a larger image. - * - * Creates an image surface for the provided pixel data. The output - * buffer must be kept around until the #cairo_surface_t is destroyed - * or cairo_surface_finish() is called on the surface. The initial - * contents of @buffer will be used as the inital image contents; you - * must explicitely clear the buffer, using, for example, - * cairo_rectangle() and cairo_fill() if you want it cleared. - * - * Return value: the newly created surface, or %NULL if it couldn't - * be created because of lack of memory - **/ -cairo_surface_t * -cairo_image_surface_create_for_data (char *data, - cairo_format_t format, - int width, - int height, - int stride) -{ - cairo_image_surface_t *surface; - pixman_format_t *pixman_format; - pixman_image_t *pixman_image; - - pixman_format = _create_pixman_format (format); - if (pixman_format == NULL) - return NULL; - - pixman_image = pixman_image_create_for_data ((pixman_bits_t *) data, pixman_format, - width, height, - _cairo_format_bpp (format), - stride); - - pixman_format_destroy (pixman_format); - - if (pixman_image == NULL) - return NULL; - - surface = _cairo_image_surface_create_for_pixman_image (pixman_image, format); - - return &surface->base; -} - -static cairo_surface_t * -_cairo_image_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - return cairo_image_surface_create (format, width, height); -} - -static void -_cairo_image_abstract_surface_destroy (void *abstract_surface) -{ - cairo_image_surface_t *surface = abstract_surface; - - if (surface->pixman_image) - pixman_image_destroy (surface->pixman_image); - - if (surface->owns_data) { - free (surface->data); - surface->data = NULL; - } - - free (surface); -} - -void -_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) -{ - surface->owns_data = 1; -} - -static double -_cairo_image_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We'll want a way to let the user set this. */ - return 96.0; -} - -static cairo_status_t -_cairo_image_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - *image_out = abstract_surface; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_image_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect_out, - void **image_extra) -{ - cairo_image_surface_t *surface = abstract_surface; - - image_rect_out->x = 0; - image_rect_out->y = 0; - image_rect_out->width = surface->width; - image_rect_out->height = surface->height; - - *image_out = surface; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_image_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - cairo_image_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_out = src; - cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -cairo_status_t -_cairo_image_surface_set_matrix (cairo_image_surface_t *surface, - cairo_matrix_t *matrix) -{ - pixman_transform_t pixman_transform; - - pixman_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - pixman_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - pixman_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - - pixman_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - pixman_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - pixman_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - - pixman_transform.matrix[2][0] = 0; - pixman_transform.matrix[2][1] = 0; - pixman_transform.matrix[2][2] = _cairo_fixed_from_double (1); - - pixman_image_set_transform (surface->pixman_image, &pixman_transform); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t filter) -{ - pixman_filter_t pixman_filter; - - switch (filter) { - case CAIRO_FILTER_FAST: - pixman_filter = PIXMAN_FILTER_FAST; - break; - case CAIRO_FILTER_GOOD: - pixman_filter = PIXMAN_FILTER_GOOD; - break; - case CAIRO_FILTER_BEST: - pixman_filter = PIXMAN_FILTER_BEST; - break; - case CAIRO_FILTER_NEAREST: - pixman_filter = PIXMAN_FILTER_NEAREST; - break; - case CAIRO_FILTER_BILINEAR: - pixman_filter = PIXMAN_FILTER_BILINEAR; - break; - default: - pixman_filter = PIXMAN_FILTER_BEST; - } - - pixman_image_set_filter (surface->pixman_image, pixman_filter); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) -{ - pixman_image_set_repeat (surface->pixman_image, repeat); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_image_surface_set_attributes (cairo_image_surface_t *surface, - cairo_surface_attributes_t *attributes) -{ - cairo_int_status_t status; - - status = _cairo_image_surface_set_matrix (surface, &attributes->matrix); - if (status) - return status; - - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - _cairo_image_surface_set_repeat (surface, 0); - break; - case CAIRO_EXTEND_REPEAT: - _cairo_image_surface_set_repeat (surface, 1); - break; - case CAIRO_EXTEND_REFLECT: - /* XXX: Obviously wrong. */ - _cairo_image_surface_set_repeat (surface, 1); - break; - } - - status = _cairo_image_surface_set_filter (surface, attributes->filter); - - return status; -} - -static pixman_operator_t -_pixman_operator (cairo_operator_t operator) -{ - switch (operator) { - case CAIRO_OPERATOR_CLEAR: - return PIXMAN_OPERATOR_CLEAR; - case CAIRO_OPERATOR_SRC: - return PIXMAN_OPERATOR_SRC; - case CAIRO_OPERATOR_DST: - return PIXMAN_OPERATOR_DST; - case CAIRO_OPERATOR_OVER: - return PIXMAN_OPERATOR_OVER; - case CAIRO_OPERATOR_OVER_REVERSE: - return PIXMAN_OPERATOR_OVER_REVERSE; - case CAIRO_OPERATOR_IN: - return PIXMAN_OPERATOR_IN; - case CAIRO_OPERATOR_IN_REVERSE: - return PIXMAN_OPERATOR_IN_REVERSE; - case CAIRO_OPERATOR_OUT: - return PIXMAN_OPERATOR_OUT; - case CAIRO_OPERATOR_OUT_REVERSE: - return PIXMAN_OPERATOR_OUT_REVERSE; - case CAIRO_OPERATOR_ATOP: - return PIXMAN_OPERATOR_ATOP; - case CAIRO_OPERATOR_ATOP_REVERSE: - return PIXMAN_OPERATOR_ATOP_REVERSE; - case CAIRO_OPERATOR_XOR: - return PIXMAN_OPERATOR_XOR; - case CAIRO_OPERATOR_ADD: - return PIXMAN_OPERATOR_ADD; - case CAIRO_OPERATOR_SATURATE: - return PIXMAN_OPERATOR_SATURATE; - default: - return PIXMAN_OPERATOR_OVER; - } -} - -static cairo_int_status_t -_cairo_image_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_surface_attributes_t src_attr, mask_attr; - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src; - cairo_image_surface_t *mask; - cairo_int_status_t status; - - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - (cairo_surface_t **) &src, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); - if (status) - return status; - - status = _cairo_image_surface_set_attributes (src, &src_attr); - if (CAIRO_OK (status)) - { - if (mask) - { - status = _cairo_image_surface_set_attributes (mask, &mask_attr); - if (CAIRO_OK (status)) - pixman_composite (_pixman_operator (operator), - src->pixman_image, - mask->pixman_image, - dst->pixman_image, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } - else - { - pixman_composite (_pixman_operator (operator), - src->pixman_image, - NULL, - dst->pixman_image, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - } - - if (mask) - _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_image_surface_t *surface = abstract_surface; - - pixman_color_t pixman_color; - - pixman_color.red = color->red_short; - pixman_color.green = color->green_short; - pixman_color.blue = color->blue_short; - pixman_color.alpha = color->alpha_short; - - /* XXX: The pixman_rectangle_t cast is evil... it needs to go away somehow. */ - pixman_fill_rectangles (_pixman_operator(operator), surface->pixman_image, - &pixman_color, (pixman_rectangle_t *) rects, num_rects); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_image_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - cairo_surface_attributes_t attributes; - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src; - cairo_int_status_t status; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - src_x, src_y, width, height, - (cairo_surface_t **) &src, - &attributes); - if (status) - return status; - - if (traps[0].left.p1.y < traps[0].left.p2.y) { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - /* XXX: The pixman_trapezoid_t cast is evil and needs to go away - * somehow. */ - status = _cairo_image_surface_set_attributes (src, &attributes); - if (CAIRO_OK (status)) - pixman_composite_trapezoids (operator, - src->pixman_image, - dst->pixman_image, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - (pixman_trapezoid_t *) traps, num_traps); - - _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_image_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_image_abstract_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface; - - return _cairo_image_surface_set_clip_region (surface, region); -} - -cairo_int_status_t -_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, - pixman_region16_t *region) -{ - if (region) { - pixman_region16_t *rcopy; - - rcopy = pixman_region_create(); - /* pixman_image_set_clip_region expects to take ownership of the - * passed-in region, so we create a copy to give it. */ - /* XXX: I think that's probably a bug in pixman. But its - * memory management issues need auditing anyway, so a - * workaround like this is fine for now. */ - pixman_region_copy (rcopy, region); - pixman_image_set_clip_region (surface->pixman_image, rcopy); - } else { - pixman_image_set_clip_region (surface->pixman_image, region); - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_is_image: - * @surface: a #cairo_surface_t - * - * Checks if a surface is an #cairo_image_surface_t - * - * Return value: True if the surface is an image surface - **/ -int -_cairo_surface_is_image (cairo_surface_t *surface) -{ - return surface->backend == &cairo_image_surface_backend; -} - -static const cairo_surface_backend_t cairo_image_surface_backend = { - _cairo_image_surface_create_similar, - _cairo_image_abstract_surface_destroy, - _cairo_image_surface_pixels_per_inch, - _cairo_image_surface_acquire_source_image, - _cairo_image_surface_release_source_image, - _cairo_image_surface_acquire_dest_image, - _cairo_image_surface_release_dest_image, - _cairo_image_surface_clone_similar, - _cairo_image_surface_composite, - _cairo_image_surface_fill_rectangles, - _cairo_image_surface_composite_trapezoids, - _cairo_image_surface_copy_page, - _cairo_image_surface_show_page, - _cairo_image_abstract_surface_set_clip_region, - NULL /* show_glyphs */ -}; diff --git a/src/cairo_matrix.c b/src/cairo_matrix.c deleted file mode 100644 index 88e536e8a..000000000 --- a/src/cairo_matrix.c +++ /dev/null @@ -1,645 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#define _GNU_SOURCE -#include <stdlib.h> -#include <math.h> - -#include "cairoint.h" - -static cairo_matrix_t const CAIRO_MATRIX_IDENTITY = { - { - {1, 0}, - {0, 1}, - {0, 0} - } -}; - -static void -_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); - -static void -_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); - -/** - * cairo_matrix_create: - * - * Creates a new identity matrix. - * - * Return value: a newly created matrix; free with cairo_matrix_destroy(), - * or %NULL if memory couldn't be allocated. - **/ -cairo_matrix_t * -cairo_matrix_create (void) -{ - cairo_matrix_t *matrix; - - matrix = malloc (sizeof (cairo_matrix_t)); - if (matrix == NULL) - return NULL; - - _cairo_matrix_init (matrix); - - return matrix; -} - -void -_cairo_matrix_init (cairo_matrix_t *matrix) -{ - cairo_matrix_set_identity (matrix); -} - -void -_cairo_matrix_fini (cairo_matrix_t *matrix) -{ - /* nothing to do here */ -} - -/** - * cairo_matrix_destroy: - * @matrix: a #cairo_matrix_t - * - * Frees a matrix created with cairo_matrix_create. - **/ -void -cairo_matrix_destroy (cairo_matrix_t *matrix) -{ - _cairo_matrix_fini (matrix); - free (matrix); -} - -/** - * cairo_matrix_copy: - * @matrix: a #cairo_matrix_t - * @other: another #cairo_ - * - * Modifies @matrix to be identical to @other. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other) -{ - *matrix = *other; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_copy); - -/** - * cairo_matrix_set_identity: - * @matrix: a #cairo_matrix_t - * - * Modifies @matrix to be an identity transformation. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_set_identity (cairo_matrix_t *matrix) -{ - *matrix = CAIRO_MATRIX_IDENTITY; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_set_identity); - -/** - * cairo_matrix_set_affine: - * @matrix: a cairo_matrix_t - * @a: a component of the affine transformation - * @b: b component of the affine transformation - * @c: c component of the affine transformation - * @d: d component of the affine transformation - * @tx: X translation component of the affine transformation - * @ty: Y translation component of the affine transformation - * - * Sets @matrix to be the affine transformation given by - * @a, b, @c, @d, @tx, @ty. The transformation is given - * by: - * <programlisting> - * x_new = x * a + y * c + tx; - * y_new = x * b + y * d + ty; - * </programlisting> - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_set_affine (cairo_matrix_t *matrix, - double a, double b, - double c, double d, - double tx, double ty) -{ - matrix->m[0][0] = a; matrix->m[0][1] = b; - matrix->m[1][0] = c; matrix->m[1][1] = d; - matrix->m[2][0] = tx; matrix->m[2][1] = ty; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_set_affine); - -/** - * cairo_matrix_get_affine: - * @matrix: a @cairo_matrix_t - * @a: location to store a component of affine transformation, or %NULL - * @b: location to store b component of affine transformation, or %NULL - * @c: location to store c component of affine transformation, or %NULL - * @d: location to store d component of affine transformation, or %NULL - * @tx: location to store X-translation component of affine transformation, or %NULL - * @ty: location to store Y-translation component of affine transformation, or %NULL - * - * Gets the matrix values for the affine tranformation that @matrix represents. - * See cairo_matrix_set_affine(). - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_get_affine (cairo_matrix_t *matrix, - double *a, double *b, - double *c, double *d, - double *tx, double *ty) -{ - if (a) - *a = matrix->m[0][0]; - if (b) - *b = matrix->m[0][1]; - - if (c) - *c = matrix->m[1][0]; - if (d) - *d = matrix->m[1][1]; - - if (tx) - *tx = matrix->m[2][0]; - if (ty) - *ty = matrix->m[2][1]; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_matrix_set_translate (cairo_matrix_t *matrix, - double tx, double ty) -{ - return cairo_matrix_set_affine (matrix, - 1, 0, - 0, 1, - tx, ty); -} - -/** - * cairo_matrix_translate: - * @matrix: a cairo_matrix_t - * @tx: amount to rotate in the X direction - * @ty: amount to rotate in the Y direction - * - * Applies a translation by @tx, @ty to the transformation in - * @matrix. The new transformation is given by first translating by - * @tx, @ty then applying the original transformation - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) -{ - cairo_matrix_t tmp; - - _cairo_matrix_set_translate (&tmp, tx, ty); - - return cairo_matrix_multiply (matrix, &tmp, matrix); -} - -cairo_status_t -_cairo_matrix_set_scale (cairo_matrix_t *matrix, - double sx, double sy) -{ - return cairo_matrix_set_affine (matrix, - sx, 0, - 0, sy, - 0, 0); -} - -/** - * cairo_matrix_scale: - * @matrix: a #cairo_matrix_t - * @sx: Scale factor in the X direction - * @sy: Scale factor in the Y direction - * - * Applies scaling by @tx, @ty to the transformation in - * @matrix. The new transformation is given by first scaling by @sx - * and @sy then applying the original transformation - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) -{ - cairo_matrix_t tmp; - - _cairo_matrix_set_scale (&tmp, sx, sy); - - return cairo_matrix_multiply (matrix, &tmp, matrix); -} -slim_hidden_def(cairo_matrix_scale); - -cairo_status_t -_cairo_matrix_set_rotate (cairo_matrix_t *matrix, - double radians) -{ - double s; - double c; -#if HAVE_SINCOS - sincos (radians, &s, &c); -#else - s = sin (radians); - c = cos (radians); -#endif - return cairo_matrix_set_affine (matrix, - c, s, - -s, c, - 0, 0); -} - -/** - * cairo_matrix_rotate: - * @matrix: a @cairo_matrix_t - * @radians: angle of rotation, in radians. Angles are defined - * so that an angle of 90 degrees (%M_PI radians) rotates the - * positive X axis into the positive Y axis. With the default - * Cairo choice of axis orientation, positive rotations are - * clockwise. - * - * Applies rotation by @radians to the transformation in - * @matrix. The new transformation is given by first rotating by - * @radians then applying the original transformation - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) -{ - cairo_matrix_t tmp; - - _cairo_matrix_set_rotate (&tmp, radians); - - return cairo_matrix_multiply (matrix, &tmp, matrix); -} - -/** - * cairo_matrix_multiply: - * @result: a @cairo_matrix_t in which to store the result - * @a: a @cairo_matrix_t - * @b: a @cairo_matrix_t - * - * Multiplies the affine transformations in @a and @b together - * and stores the result in @result. The resulting transformation - * is given by first applying the transformation in @b then - * applying the transformation in @a. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) -{ - cairo_matrix_t r; - int row, col, n; - double t; - - for (row = 0; row < 3; row++) { - for (col = 0; col < 2; col++) { - if (row == 2) - t = b->m[2][col]; - else - t = 0; - for (n = 0; n < 2; n++) { - t += a->m[row][n] * b->m[n][col]; - } - r.m[row][col] = t; - } - } - - *result = r; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_multiply); - -/** - * cairo_matrix_transform_distance: - * @matrix: a @cairo_matrix_t - * @dx: a distance in the X direction. An in/out parameter - * @dy: a distance in the Y direction. An in/out parameter - * - * Transforms the vector (@dx,@dy) by @matrix. Translation is - * ignored. In terms of the components of the affine transformation: - * - * <programlisting> - * dx2 = dx1 * a + dy1 * c; - * dy2 = dx1 * b + dy1 * d; - * </programlisting> - * - * Affine transformations are position invariant, so the same vector - * always transforms to the same vector. If (@x1,@y1) transforms - * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to - * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy) -{ - double new_x, new_y; - - new_x = (matrix->m[0][0] * *dx - + matrix->m[1][0] * *dy); - new_y = (matrix->m[0][1] * *dx - + matrix->m[1][1] * *dy); - - *dx = new_x; - *dy = new_y; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_transform_distance); - -/** - * cairo_matrix_transform_point: - * @matrix: a @cairo_matrix_t - * @x: X position. An in/out parameter - * @y: Y position. An in/out parameter - * - * Transforms the point (@x, @y) by @matrix. - * - * Return value: %CAIRO_STATUS_SUCCESS, always. - **/ -cairo_status_t -cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y) -{ - cairo_matrix_transform_distance (matrix, x, y); - - *x += matrix->m[2][0]; - *y += matrix->m[2][1]; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_transform_point); - -cairo_status_t -_cairo_matrix_transform_bounding_box (cairo_matrix_t *matrix, - double *x, double *y, - double *width, double *height) -{ - int i; - double quad_x[4], quad_y[4]; - double dx1, dy1; - double dx2, dy2; - double min_x, max_x; - double min_y, max_y; - - quad_x[0] = *x; - quad_y[0] = *y; - cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]); - - dx1 = *width; - dy1 = 0; - cairo_matrix_transform_distance (matrix, &dx1, &dy1); - quad_x[1] = quad_x[0] + dx1; - quad_y[1] = quad_y[0] + dy1; - - dx2 = 0; - dy2 = *height; - cairo_matrix_transform_distance (matrix, &dx2, &dy2); - quad_x[2] = quad_x[0] + dx2; - quad_y[2] = quad_y[0] + dy2; - - quad_x[3] = quad_x[0] + dx1 + dx2; - quad_y[3] = quad_y[0] + dy1 + dy2; - - min_x = max_x = quad_x[0]; - min_y = max_y = quad_y[0]; - - for (i=1; i < 4; i++) { - if (quad_x[i] < min_x) - min_x = quad_x[i]; - if (quad_x[i] > max_x) - max_x = quad_x[i]; - - if (quad_y[i] < min_y) - min_y = quad_y[i]; - if (quad_y[i] > max_y) - max_y = quad_y[i]; - } - - *x = min_x; - *y = min_y; - *width = max_x - min_x; - *height = max_y - min_y; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar) -{ - int row, col; - - for (row = 0; row < 3; row++) - for (col = 0; col < 2; col++) - matrix->m[row][col] *= scalar; -} - -/* This function isn't a correct adjoint in that the implicit 1 in the - homogeneous result should actually be ad-bc instead. But, since this - adjoint is only used in the computation of the inverse, which - divides by det (A)=ad-bc anyway, everything works out in the end. */ -static void -_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) -{ - /* adj (A) = transpose (C:cofactor (A,i,j)) */ - double a, b, c, d, tx, ty; - - a = matrix->m[0][0]; b = matrix->m[0][1]; - c = matrix->m[1][0]; d = matrix->m[1][1]; - tx = matrix->m[2][0]; ty = matrix->m[2][1]; - - cairo_matrix_set_affine (matrix, - d, -b, - -c, a, - c*ty - d*tx, b*tx - a*ty); -} - -/** - * cairo_matrix_invert: - * @matrix: a @cairo_matrix_t - * - * Changes @matrix to be the inverse of it's original value. Not - * all transformation matrices have inverses; if the matrix - * collapses points together (it is <firstterm>degenerate</firstterm>), - * then it has no inverse and this function will fail. - * - * Returns: If @matrix has an inverse, modifies @matrix to - * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, - * returns %CAIRO_STATUS_INVALID_MATRIX. - **/ -cairo_status_t -cairo_matrix_invert (cairo_matrix_t *matrix) -{ - /* inv (A) = 1/det (A) * adj (A) */ - double det; - - _cairo_matrix_compute_determinant (matrix, &det); - - if (det == 0) - return CAIRO_STATUS_INVALID_MATRIX; - - _cairo_matrix_compute_adjoint (matrix); - _cairo_matrix_scalar_multiply (matrix, 1 / det); - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_matrix_invert); - -cairo_status_t -_cairo_matrix_compute_determinant (cairo_matrix_t *matrix, double *det) -{ - double a, b, c, d; - - a = matrix->m[0][0]; b = matrix->m[0][1]; - c = matrix->m[1][0]; d = matrix->m[1][1]; - - *det = a*d - b*c; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, double *lambda2) -{ - /* The eigenvalues of an NxN matrix M are found by solving the polynomial: - - det (M - lI) = 0 - - The zeros in our homogeneous 3x3 matrix make this equation equal - to that formed by the sub-matrix: - - M = a b - c d - - by which: - - l^2 - (a+d)l + (ad - bc) = 0 - - l = (a+d +/- sqrt (a^2 + 2ad + d^2 - 4 (ad-bc))) / 2; - */ - - double a, b, c, d, rad; - - a = matrix->m[0][0]; - b = matrix->m[0][1]; - c = matrix->m[1][0]; - d = matrix->m[1][1]; - - rad = sqrt (a*a + 2*a*d + d*d - 4*(a*d - b*c)); - *lambda1 = (a + d + rad) / 2.0; - *lambda2 = (a + d - rad) / 2.0; - - return CAIRO_STATUS_SUCCESS; -} - -/* Compute the amount that each basis vector is scaled by. */ -cairo_status_t -_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major) -{ - double det; - - _cairo_matrix_compute_determinant (matrix, &det); - - if (det == 0) - *sx = *sy = 0; - else - { - double x = x_major != 0; - double y = x == 0; - double major, minor; - - cairo_matrix_transform_distance (matrix, &x, &y); - major = sqrt(x*x + y*y); - /* - * ignore mirroring - */ - if (det < 0) - det = -det; - if (major) - minor = det / major; - else - minor = 0.0; - if (x_major) - { - *sx = major; - *sy = minor; - } - else - { - *sx = minor; - *sy = major; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_bool_t -_cairo_matrix_is_integer_translation(cairo_matrix_t *mat, - int *itx, int *ity) -{ - double a, b, c, d, tx, ty; - int ttx, tty; - int ok = 0; - cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); - ttx = _cairo_fixed_from_double (tx); - tty = _cairo_fixed_from_double (ty); - ok = ((a == 1.0) - && (b == 0.0) - && (c == 0.0) - && (d == 1.0) - && (_cairo_fixed_is_integer(ttx)) - && (_cairo_fixed_is_integer(tty))); - if (ok) { - *itx = _cairo_fixed_integer_part(ttx); - *ity = _cairo_fixed_integer_part(tty); - return TRUE; - } - return FALSE; -} diff --git a/src/cairo_path.c b/src/cairo_path.c deleted file mode 100644 index 8314f601c..000000000 --- a/src/cairo_path.c +++ /dev/null @@ -1,495 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include <stdlib.h> -#include "cairoint.h" - -/* private functions */ -static cairo_status_t -_cairo_path_add (cairo_path_t *path, cairo_path_op_t op, cairo_point_t *points, int num_pts); - -static void -_cairo_path_add_op_buf (cairo_path_t *path, cairo_path_op_buf_t *op); - -static cairo_status_t -_cairo_path_new_op_buf (cairo_path_t *path); - -static void -_cairo_path_add_arg_buf (cairo_path_t *path, cairo_path_arg_buf_t *arg); - -static cairo_status_t -_cairo_path_new_arg_buf (cairo_path_t *path); - -static cairo_path_op_buf_t * -_cairo_path_op_buf_create (void); - -static void -_cairo_path_op_buf_destroy (cairo_path_op_buf_t *buf); - -static void -_cairo_path_op_buf_add (cairo_path_op_buf_t *op_buf, cairo_path_op_t op); - -static cairo_path_arg_buf_t * -_cairo_path_arg_buf_create (void); - -static void -_cairo_path_arg_buf_destroy (cairo_path_arg_buf_t *buf); - -static void -_cairo_path_arg_buf_add (cairo_path_arg_buf_t *arg, cairo_point_t *points, int num_points); - -void -_cairo_path_init (cairo_path_t *path) -{ - path->op_head = NULL; - path->op_tail = NULL; - - path->arg_head = NULL; - path->arg_tail = NULL; - - path->current_point.x = 0; - path->current_point.y = 0; - path->has_current_point = 0; - path->last_move_point = path->current_point; -} - -cairo_status_t -_cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) -{ - cairo_path_op_buf_t *op, *other_op; - cairo_path_arg_buf_t *arg, *other_arg; - - _cairo_path_init (path); - path->current_point = other->current_point; - path->has_current_point = other->has_current_point; - path->last_move_point = other->last_move_point; - - for (other_op = other->op_head; other_op; other_op = other_op->next) { - op = _cairo_path_op_buf_create (); - if (op == NULL) { - _cairo_path_fini(path); - return CAIRO_STATUS_NO_MEMORY; - } - *op = *other_op; - _cairo_path_add_op_buf (path, op); - } - - for (other_arg = other->arg_head; other_arg; other_arg = other_arg->next) { - arg = _cairo_path_arg_buf_create (); - if (arg == NULL) { - _cairo_path_fini(path); - return CAIRO_STATUS_NO_MEMORY; - } - *arg = *other_arg; - _cairo_path_add_arg_buf (path, arg); - } - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_path_fini (cairo_path_t *path) -{ - cairo_path_op_buf_t *op; - cairo_path_arg_buf_t *arg; - - while (path->op_head) { - op = path->op_head; - path->op_head = op->next; - _cairo_path_op_buf_destroy (op); - } - path->op_tail = NULL; - - while (path->arg_head) { - arg = path->arg_head; - path->arg_head = arg->next; - _cairo_path_arg_buf_destroy (arg); - } - path->arg_tail = NULL; - - path->has_current_point = 0; -} - -cairo_status_t -_cairo_path_move_to (cairo_path_t *path, cairo_point_t *point) -{ - cairo_status_t status; - - status = _cairo_path_add (path, CAIRO_PATH_OP_MOVE_TO, point, 1); - if (status) - return status; - - path->current_point = *point; - path->has_current_point = 1; - path->last_move_point = path->current_point; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_rel_move_to (cairo_path_t *path, cairo_distance_t *distance) -{ - cairo_point_t point; - - point.x = path->current_point.x + distance->dx; - point.y = path->current_point.y + distance->dy; - - return _cairo_path_move_to (path, &point); -} - -cairo_status_t -_cairo_path_line_to (cairo_path_t *path, cairo_point_t *point) -{ - cairo_status_t status; - - status = _cairo_path_add (path, CAIRO_PATH_OP_LINE_TO, point, 1); - if (status) - return status; - - path->current_point = *point; - path->has_current_point = 1; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_rel_line_to (cairo_path_t *path, cairo_distance_t *distance) -{ - cairo_point_t point; - - point.x = path->current_point.x + distance->dx; - point.y = path->current_point.y + distance->dy; - - return _cairo_path_line_to (path, &point); -} - -cairo_status_t -_cairo_path_curve_to (cairo_path_t *path, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2) -{ - cairo_status_t status; - cairo_point_t point[3]; - - point[0] = *p0; - point[1] = *p1; - point[2] = *p2; - - status = _cairo_path_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); - if (status) - return status; - - path->current_point = *p2; - path->has_current_point = 1; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_rel_curve_to (cairo_path_t *path, - cairo_distance_t *d0, - cairo_distance_t *d1, - cairo_distance_t *d2) -{ - cairo_point_t p0, p1, p2; - - p0.x = path->current_point.x + d0->dx; - p0.y = path->current_point.y + d0->dy; - - p1.x = path->current_point.x + d1->dx; - p1.y = path->current_point.y + d1->dy; - - p2.x = path->current_point.x + d2->dx; - p2.y = path->current_point.y + d2->dy; - - return _cairo_path_curve_to (path, &p0, &p1, &p2); -} - -cairo_status_t -_cairo_path_close_path (cairo_path_t *path) -{ - cairo_status_t status; - - status = _cairo_path_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); - if (status) - return status; - - path->current_point.x = path->last_move_point.x; - path->current_point.y = path->last_move_point.y; - path->has_current_point = 1; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_current_point (cairo_path_t *path, cairo_point_t *point) -{ - if (! path->has_current_point) - return CAIRO_STATUS_NO_CURRENT_POINT; - - *point = path->current_point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_add (cairo_path_t *path, cairo_path_op_t op, cairo_point_t *points, int num_points) -{ - cairo_status_t status; - - if (path->op_tail == NULL || path->op_tail->num_ops + 1 > CAIRO_PATH_BUF_SZ) { - status = _cairo_path_new_op_buf (path); - if (status) - return status; - } - _cairo_path_op_buf_add (path->op_tail, op); - - if (path->arg_tail == NULL || path->arg_tail->num_points + num_points > CAIRO_PATH_BUF_SZ) { - status = _cairo_path_new_arg_buf (path); - if (status) - return status; - } - _cairo_path_arg_buf_add (path->arg_tail, points, num_points); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_path_add_op_buf (cairo_path_t *path, cairo_path_op_buf_t *op) -{ - op->next = NULL; - op->prev = path->op_tail; - - if (path->op_tail) { - path->op_tail->next = op; - } else { - path->op_head = op; - } - - path->op_tail = op; -} - -static cairo_status_t -_cairo_path_new_op_buf (cairo_path_t *path) -{ - cairo_path_op_buf_t *op; - - op = _cairo_path_op_buf_create (); - if (op == NULL) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_path_add_op_buf (path, op); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_path_add_arg_buf (cairo_path_t *path, cairo_path_arg_buf_t *arg) -{ - arg->next = NULL; - arg->prev = path->arg_tail; - - if (path->arg_tail) { - path->arg_tail->next = arg; - } else { - path->arg_head = arg; - } - - path->arg_tail = arg; -} - -static cairo_status_t -_cairo_path_new_arg_buf (cairo_path_t *path) -{ - cairo_path_arg_buf_t *arg; - - arg = _cairo_path_arg_buf_create (); - - if (arg == NULL) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_path_add_arg_buf (path, arg); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_path_op_buf_t * -_cairo_path_op_buf_create (void) -{ - cairo_path_op_buf_t *op; - - op = malloc (sizeof (cairo_path_op_buf_t)); - - if (op) { - op->num_ops = 0; - op->next = NULL; - } - - return op; -} - -static void -_cairo_path_op_buf_destroy (cairo_path_op_buf_t *op) -{ - free (op); -} - -static void -_cairo_path_op_buf_add (cairo_path_op_buf_t *op_buf, cairo_path_op_t op) -{ - op_buf->op[op_buf->num_ops++] = op; -} - -static cairo_path_arg_buf_t * -_cairo_path_arg_buf_create (void) -{ - cairo_path_arg_buf_t *arg; - - arg = malloc (sizeof (cairo_path_arg_buf_t)); - - if (arg) { - arg->num_points = 0; - arg->next = NULL; - } - - return arg; -} - -static void -_cairo_path_arg_buf_destroy (cairo_path_arg_buf_t *arg) -{ - free (arg); -} - -static void -_cairo_path_arg_buf_add (cairo_path_arg_buf_t *arg, cairo_point_t *points, int num_points) -{ - int i; - - for (i=0; i < num_points; i++) { - arg->points[arg->num_points++] = points[i]; - } -} - -#define CAIRO_PATH_OP_MAX_ARGS 3 - -static int const num_args[] = -{ - 1, /* cairo_path_move_to */ - 1, /* cairo_path_op_line_to */ - 3, /* cairo_path_op_curve_to */ - 0, /* cairo_path_op_close_path */ -}; - -cairo_status_t -_cairo_path_interpret (cairo_path_t *path, - cairo_direction_t dir, - cairo_path_move_to_func_t *move_to, - cairo_path_line_to_func_t *line_to, - cairo_path_curve_to_func_t *curve_to, - cairo_path_close_path_func_t *close_path, - void *closure) -{ - cairo_status_t status; - int i, arg; - cairo_path_op_buf_t *op_buf; - cairo_path_op_t op; - cairo_path_arg_buf_t *arg_buf = path->arg_head; - int buf_i = 0; - cairo_point_t point[CAIRO_PATH_OP_MAX_ARGS]; - int step = (dir == CAIRO_DIRECTION_FORWARD) ? 1 : -1; - - for (op_buf = (dir == CAIRO_DIRECTION_FORWARD) ? path->op_head : path->op_tail; - op_buf; - op_buf = (dir == CAIRO_DIRECTION_FORWARD) ? op_buf->next : op_buf->prev) - { - int start, stop; - if (dir == CAIRO_DIRECTION_FORWARD) { - start = 0; - stop = op_buf->num_ops; - } else { - start = op_buf->num_ops - 1; - stop = -1; - } - - for (i=start; i != stop; i += step) { - op = op_buf->op[i]; - - if (dir == CAIRO_DIRECTION_REVERSE) { - if (buf_i == 0) { - arg_buf = arg_buf->prev; - buf_i = arg_buf->num_points; - } - buf_i -= num_args[op]; - } - - for (arg = 0; arg < num_args[op]; arg++) { - point[arg] = arg_buf->points[buf_i]; - buf_i++; - if (buf_i >= arg_buf->num_points) { - arg_buf = arg_buf->next; - buf_i = 0; - } - } - - if (dir == CAIRO_DIRECTION_REVERSE) { - buf_i -= num_args[op]; - } - - switch (op) { - case CAIRO_PATH_OP_MOVE_TO: - status = (*move_to) (closure, &point[0]); - break; - case CAIRO_PATH_OP_LINE_TO: - status = (*line_to) (closure, &point[0]); - break; - case CAIRO_PATH_OP_CURVE_TO: - status = (*curve_to) (closure, &point[0], &point[1], &point[2]); - break; - case CAIRO_PATH_OP_CLOSE_PATH: - default: - status = (*close_path) (closure); - break; - } - if (status) - return status; - } - } - - return CAIRO_STATUS_SUCCESS; -} - diff --git a/src/cairo_path_bounds.c b/src/cairo_path_bounds.c deleted file mode 100644 index 7c5772a82..000000000 --- a/src/cairo_path_bounds.c +++ /dev/null @@ -1,182 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2003 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -typedef struct cairo_path_bounder { - int has_point; - - cairo_fixed_t min_x; - cairo_fixed_t min_y; - cairo_fixed_t max_x; - cairo_fixed_t max_y; -} cairo_path_bounder_t; - -static void -_cairo_path_bounder_init (cairo_path_bounder_t *bounder); - -static void -_cairo_path_bounder_fini (cairo_path_bounder_t *bounder); - -static cairo_status_t -_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *point); - -static cairo_status_t -_cairo_path_bounder_move_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_path_bounder_line_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_path_bounder_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -static cairo_status_t -_cairo_path_bounder_close_path (void *closure); - -static void -_cairo_path_bounder_init (cairo_path_bounder_t *bounder) -{ - bounder->has_point = 0; -} - -static void -_cairo_path_bounder_fini (cairo_path_bounder_t *bounder) -{ - bounder->has_point = 0; -} - -static cairo_status_t -_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *point) -{ - if (bounder->has_point) { - if (point->x < bounder->min_x) - bounder->min_x = point->x; - - if (point->y < bounder->min_y) - bounder->min_y = point->y; - - if (point->x > bounder->max_x) - bounder->max_x = point->x; - - if (point->y > bounder->max_y) - bounder->max_y = point->y; - } else { - bounder->min_x = point->x; - bounder->min_y = point->y; - bounder->max_x = point->x; - bounder->max_y = point->y; - - bounder->has_point = 1; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_bounder_move_to (void *closure, cairo_point_t *point) -{ - cairo_path_bounder_t *bounder = closure; - - _cairo_path_bounder_add_point (bounder, point); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_bounder_line_to (void *closure, cairo_point_t *point) -{ - cairo_path_bounder_t *bounder = closure; - - _cairo_path_bounder_add_point (bounder, point); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_bounder_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) -{ - cairo_path_bounder_t *bounder = closure; - - _cairo_path_bounder_add_point (bounder, b); - _cairo_path_bounder_add_point (bounder, c); - _cairo_path_bounder_add_point (bounder, d); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_path_bounder_close_path (void *closure) -{ - return CAIRO_STATUS_SUCCESS; -} - -/* XXX: Perhaps this should compute a PixRegion rather than 4 doubles */ -cairo_status_t -_cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2) -{ - cairo_status_t status; - - cairo_path_bounder_t bounder; - - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - if (status) { - *x1 = *y1 = *x2 = *y2 = 0.0; - _cairo_path_bounder_fini (&bounder); - return status; - } - - *x1 = _cairo_fixed_to_double (bounder.min_x); - *y1 = _cairo_fixed_to_double (bounder.min_y); - *x2 = _cairo_fixed_to_double (bounder.max_x); - *y2 = _cairo_fixed_to_double (bounder.max_y); - - _cairo_path_bounder_fini (&bounder); - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo_path_fill.c b/src/cairo_path_fill.c deleted file mode 100644 index dc79b6b96..000000000 --- a/src/cairo_path_fill.c +++ /dev/null @@ -1,206 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -typedef struct cairo_filler { - cairo_gstate_t *gstate; - cairo_traps_t *traps; - - cairo_point_t current_point; - - cairo_polygon_t polygon; -} cairo_filler_t; - -static void -_cairo_filler_init (cairo_filler_t *filler, cairo_gstate_t *gstate, cairo_traps_t *traps); - -static void -_cairo_filler_fini (cairo_filler_t *filler); - -static cairo_status_t -_cairo_filler_move_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_filler_line_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_filler_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -static cairo_status_t -_cairo_filler_close_path (void *closure); - -static void -_cairo_filler_init (cairo_filler_t *filler, cairo_gstate_t *gstate, cairo_traps_t *traps) -{ - filler->gstate = gstate; - filler->traps = traps; - - filler->current_point.x = 0; - filler->current_point.y = 0; - - _cairo_polygon_init (&filler->polygon); -} - -static void -_cairo_filler_fini (cairo_filler_t *filler) -{ - _cairo_polygon_fini (&filler->polygon); -} - -static cairo_status_t -_cairo_filler_move_to (void *closure, cairo_point_t *point) -{ - cairo_status_t status; - cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; - - status = _cairo_polygon_close (polygon); - if (status) - return status; - - status = _cairo_polygon_move_to (polygon, point); - if (status) - return status; - - filler->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_filler_line_to (void *closure, cairo_point_t *point) -{ - cairo_status_t status; - cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; - - status = _cairo_polygon_line_to (polygon, point); - if (status) - return status; - - filler->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_filler_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) -{ - int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; - cairo_gstate_t *gstate = filler->gstate; - cairo_spline_t spline; - - status = _cairo_spline_init (&spline, &filler->current_point, b, c, d); - - if (status == CAIRO_INT_STATUS_DEGENERATE) - return CAIRO_STATUS_SUCCESS; - - _cairo_spline_decompose (&spline, gstate->tolerance); - if (status) - goto CLEANUP_SPLINE; - - for (i = 1; i < spline.num_points; i++) { - status = _cairo_polygon_line_to (polygon, &spline.points[i]); - if (status) - break; - } - - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); - - filler->current_point = *d; - - return status; -} - -static cairo_status_t -_cairo_filler_close_path (void *closure) -{ - cairo_status_t status; - cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; - - status = _cairo_polygon_close (polygon); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_fill_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_filler_t filler; - - _cairo_filler_init (&filler, gstate, traps); - - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_filler_move_to, - _cairo_filler_line_to, - _cairo_filler_curve_to, - _cairo_filler_close_path, - &filler); - if (status) - goto BAIL; - - status = _cairo_polygon_close (&filler.polygon); - if (status) - goto BAIL; - - status = _cairo_traps_tessellate_polygon (filler.traps, - &filler.polygon, - filler.gstate->fill_rule); - if (status) - goto BAIL; - -BAIL: - _cairo_filler_fini (&filler); - - return status; -} - diff --git a/src/cairo_path_stroke.c b/src/cairo_path_stroke.c deleted file mode 100644 index 08b380902..000000000 --- a/src/cairo_path_stroke.c +++ /dev/null @@ -1,848 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -typedef struct cairo_stroker { - cairo_gstate_t *gstate; - cairo_traps_t *traps; - - int has_current_point; - cairo_point_t current_point; - cairo_point_t first_point; - - int has_current_face; - cairo_stroke_face_t current_face; - - int has_first_face; - cairo_stroke_face_t first_face; - - int dashed; - int dash_index; - int dash_on; - double dash_remain; -} cairo_stroker_t; - -/* private functions */ -static void -_cairo_stroker_init (cairo_stroker_t *stroker, cairo_gstate_t *gstate, cairo_traps_t *traps); - -static void -_cairo_stroker_fini (cairo_stroker_t *stroker); - -static cairo_status_t -_cairo_stroker_move_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_stroker_line_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_stroker_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -static cairo_status_t -_cairo_stroker_close_path (void *closure); - -static void -_translate_point (cairo_point_t *point, cairo_point_t *offset); - -static int -_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out); - -static cairo_status_t -_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out); - -static void -_cairo_stroker_start_dash (cairo_stroker_t *stroker) -{ - cairo_gstate_t *gstate = stroker->gstate; - double offset; - int on = 1; - int i = 0; - - offset = gstate->dash_offset; - while (offset >= gstate->dash[i]) { - offset -= gstate->dash[i]; - on = 1-on; - if (++i == gstate->num_dashes) - i = 0; - } - stroker->dashed = 1; - stroker->dash_index = i; - stroker->dash_on = on; - stroker->dash_remain = gstate->dash[i] - offset; -} - -static void -_cairo_stroker_step_dash (cairo_stroker_t *stroker, double step) -{ - cairo_gstate_t *gstate = stroker->gstate; - stroker->dash_remain -= step; - if (stroker->dash_remain <= 0) { - stroker->dash_index++; - if (stroker->dash_index == gstate->num_dashes) - stroker->dash_index = 0; - stroker->dash_on = 1-stroker->dash_on; - stroker->dash_remain = gstate->dash[stroker->dash_index]; - } -} - -static void -_cairo_stroker_init (cairo_stroker_t *stroker, cairo_gstate_t *gstate, cairo_traps_t *traps) -{ - stroker->gstate = gstate; - stroker->traps = traps; - - stroker->has_current_point = 0; - stroker->has_current_face = 0; - stroker->has_first_face = 0; - - if (gstate->dash) - _cairo_stroker_start_dash (stroker); - else - stroker->dashed = 0; -} - -static void -_cairo_stroker_fini (cairo_stroker_t *stroker) -{ - /* nothing to do here */ -} - -static void -_translate_point (cairo_point_t *point, cairo_point_t *offset) -{ - point->x += offset->x; - point->y += offset->y; -} - -static int -_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out) -{ - cairo_slope_t in_slope, out_slope; - - _cairo_slope_init (&in_slope, &in->point, &in->cw); - _cairo_slope_init (&out_slope, &out->point, &out->cw); - - return _cairo_slope_clockwise (&in_slope, &out_slope); -} - -static cairo_status_t -_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out) -{ - cairo_status_t status; - cairo_gstate_t *gstate = stroker->gstate; - int clockwise = _cairo_stroker_face_clockwise (out, in); - cairo_point_t *inpt, *outpt; - - if (in->cw.x == out->cw.x - && in->cw.y == out->cw.y - && in->ccw.x == out->ccw.x - && in->ccw.y == out->ccw.y) { - return CAIRO_STATUS_SUCCESS; - } - - if (clockwise) { - inpt = &in->ccw; - outpt = &out->ccw; - } else { - inpt = &in->cw; - outpt = &out->cw; - } - - switch (gstate->line_join) { - case CAIRO_LINE_JOIN_ROUND: { - int i; - int start, step, stop; - cairo_point_t tri[3]; - cairo_pen_t *pen = &gstate->pen_regular; - - tri[0] = in->point; - if (clockwise) { - _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector, &start); - step = -1; - _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector, &stop); - } else { - _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector, &start); - step = +1; - _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector, &stop); - } - - i = start; - tri[1] = *inpt; - while (i != stop) { - tri[2] = in->point; - _translate_point (&tri[2], &pen->vertices[i].point); - _cairo_traps_tessellate_triangle (stroker->traps, tri); - tri[1] = tri[2]; - i += step; - if (i < 0) - i = pen->num_vertices - 1; - if (i >= pen->num_vertices) - i = 0; - } - - tri[2] = *outpt; - - return _cairo_traps_tessellate_triangle (stroker->traps, tri); - } - case CAIRO_LINE_JOIN_MITER: - default: { - /* dot product of incoming slope vector with outgoing slope vector */ - double in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+ - (-in->usr_vector.y * out->usr_vector.y)); - double ml = gstate->miter_limit; - - /* - * Check the miter limit -- lines meeting at an acute angle - * can generate long miters, the limit converts them to bevel - * - * We want to know when the miter is within the miter limit. - * That's straightforward to specify: - * - * secant (psi / 2) <= ml - * - * where psi is the angle between in and out - * - * secant(psi/2) = 1/sin(psi/2) - * 1/sin(psi/2) <= ml - * 1 <= ml sin(psi/2) - * 1 <= ml² sin²(psi/2) - * 2 <= ml² 2 sin²(psi/2) - * 2·sin²(psi/2) = 1-cos(psi) - * 2 <= ml² (1-cos(psi)) - * - * in · out = |in| |out| cos (psi) - * - * in and out are both unit vectors, so: - * - * in · out = cos (psi) - * - * 2 <= ml² (1 - in · out) - * - */ - if (2 <= ml * ml * (1 - in_dot_out)) { - double x1, y1, x2, y2; - double mx, my; - double dx1, dx2, dy1, dy2; - cairo_polygon_t polygon; - cairo_point_t outer; - - /* - * we've got the points already transformed to device - * space, but need to do some computation with them and - * also need to transform the slope from user space to - * device space - */ - /* outer point of incoming line face */ - x1 = _cairo_fixed_to_double (inpt->x); - y1 = _cairo_fixed_to_double (inpt->y); - dx1 = in->usr_vector.x; - dy1 = in->usr_vector.y; - cairo_matrix_transform_distance (&gstate->ctm, &dx1, &dy1); - - /* outer point of outgoing line face */ - x2 = _cairo_fixed_to_double (outpt->x); - y2 = _cairo_fixed_to_double (outpt->y); - dx2 = out->usr_vector.x; - dy2 = out->usr_vector.y; - cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); - - /* - * Compute the location of the outer corner of the miter. - * That's pretty easy -- just the intersection of the two - * outer edges. We've got slopes and points on each - * of those edges. Compute my directly, then compute - * mx by using the edge with the larger dy; that avoids - * dividing by values close to zero. - */ - my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / - (dx1 * dy2 - dx2 * dy1)); - if (fabs (dy1) >= fabs (dy2)) - mx = (my - y1) * dx1 / dy1 + x1; - else - mx = (my - y2) * dx2 / dy2 + x2; - - /* - * Draw the quadrilateral - */ - outer.x = _cairo_fixed_from_double (mx); - outer.y = _cairo_fixed_from_double (my); - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &in->point); - _cairo_polygon_line_to (&polygon, inpt); - _cairo_polygon_line_to (&polygon, &outer); - _cairo_polygon_line_to (&polygon, outpt); - _cairo_polygon_close (&polygon); - status = _cairo_traps_tessellate_polygon (stroker->traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - _cairo_polygon_fini (&polygon); - - return status; - } - /* fall through ... */ - } - case CAIRO_LINE_JOIN_BEVEL: { - cairo_point_t tri[3]; - tri[0] = in->point; - tri[1] = *inpt; - tri[2] = *outpt; - - return _cairo_traps_tessellate_triangle (stroker->traps, tri); - } - } -} - -static cairo_status_t -_cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) -{ - cairo_status_t status; - cairo_gstate_t *gstate = stroker->gstate; - - if (gstate->line_cap == CAIRO_LINE_CAP_BUTT) - return CAIRO_STATUS_SUCCESS; - - switch (gstate->line_cap) { - case CAIRO_LINE_CAP_ROUND: { - int i; - int start, stop; - cairo_slope_t slope; - cairo_point_t tri[3]; - cairo_pen_t *pen = &gstate->pen_regular; - - slope = f->dev_vector; - _cairo_pen_find_active_cw_vertex_index (pen, &slope, &start); - slope.dx = -slope.dx; - slope.dy = -slope.dy; - _cairo_pen_find_active_cw_vertex_index (pen, &slope, &stop); - - tri[0] = f->point; - tri[1] = f->cw; - for (i=start; i != stop; i = (i+1) % pen->num_vertices) { - tri[2] = f->point; - _translate_point (&tri[2], &pen->vertices[i].point); - _cairo_traps_tessellate_triangle (stroker->traps, tri); - tri[1] = tri[2]; - } - tri[2] = f->ccw; - - return _cairo_traps_tessellate_triangle (stroker->traps, tri); - } - case CAIRO_LINE_CAP_SQUARE: { - double dx, dy; - cairo_slope_t fvector; - cairo_point_t occw, ocw; - cairo_polygon_t polygon; - - dx = f->usr_vector.x; - dy = f->usr_vector.y; - dx *= gstate->line_width / 2.0; - dy *= gstate->line_width / 2.0; - cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - fvector.dx = _cairo_fixed_from_double (dx); - fvector.dy = _cairo_fixed_from_double (dy); - occw.x = f->ccw.x + fvector.dx; - occw.y = f->ccw.y + fvector.dy; - ocw.x = f->cw.x + fvector.dx; - ocw.y = f->cw.y + fvector.dy; - - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &f->cw); - _cairo_polygon_line_to (&polygon, &ocw); - _cairo_polygon_line_to (&polygon, &occw); - _cairo_polygon_line_to (&polygon, &f->ccw); - _cairo_polygon_close (&polygon); - - status = _cairo_traps_tessellate_polygon (stroker->traps, &polygon, CAIRO_FILL_RULE_WINDING); - _cairo_polygon_fini (&polygon); - - return status; - } - case CAIRO_LINE_CAP_BUTT: - default: - return CAIRO_STATUS_SUCCESS; - } -} - -static void -_compute_face (cairo_point_t *point, cairo_slope_t *slope, cairo_gstate_t *gstate, cairo_stroke_face_t *face) -{ - double mag, det; - double line_dx, line_dy; - double face_dx, face_dy; - cairo_point_double_t usr_vector; - cairo_point_t offset_ccw, offset_cw; - - line_dx = _cairo_fixed_to_double (slope->dx); - line_dy = _cairo_fixed_to_double (slope->dy); - - /* faces are normal in user space, not device space */ - cairo_matrix_transform_distance (&gstate->ctm_inverse, &line_dx, &line_dy); - - mag = sqrt (line_dx * line_dx + line_dy * line_dy); - if (mag == 0) { - /* XXX: Can't compute other face points. Do we want a tag in the face for this case? */ - return; - } - - /* normalize to unit length */ - line_dx /= mag; - line_dy /= mag; - - usr_vector.x = line_dx; - usr_vector.y = line_dy; - - /* - * rotate to get a line_width/2 vector along the face, note that - * the vector must be rotated the right direction in device space, - * but by 90° in user space. So, the rotation depends on - * whether the ctm reflects or not, and that can be determined - * by looking at the determinant of the matrix. - */ - _cairo_matrix_compute_determinant (&gstate->ctm, &det); - if (det >= 0) - { - face_dx = - line_dy * (gstate->line_width / 2.0); - face_dy = line_dx * (gstate->line_width / 2.0); - } - else - { - face_dx = line_dy * (gstate->line_width / 2.0); - face_dy = - line_dx * (gstate->line_width / 2.0); - } - - /* back to device space */ - cairo_matrix_transform_distance (&gstate->ctm, &face_dx, &face_dy); - - offset_ccw.x = _cairo_fixed_from_double (face_dx); - offset_ccw.y = _cairo_fixed_from_double (face_dy); - offset_cw.x = -offset_ccw.x; - offset_cw.y = -offset_ccw.y; - - face->ccw = *point; - _translate_point (&face->ccw, &offset_ccw); - - face->point = *point; - - face->cw = *point; - _translate_point (&face->cw, &offset_cw); - - face->usr_vector.x = usr_vector.x; - face->usr_vector.y = usr_vector.y; - - face->dev_vector = *slope; -} - -static cairo_status_t -_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_point_t *p2, - cairo_stroke_face_t *start, cairo_stroke_face_t *end) -{ - cairo_status_t status; - cairo_gstate_t *gstate = stroker->gstate; - cairo_polygon_t polygon; - cairo_slope_t slope; - - if (p1->x == p2->x && p1->y == p2->y) { - /* XXX: Need to rethink how this case should be handled, (both - here and in _compute_face). The key behavior is that - degenerate paths should draw as much as possible. */ - return CAIRO_STATUS_SUCCESS; - } - - _cairo_slope_init (&slope, p1, p2); - _compute_face (p1, &slope, gstate, start); - - /* XXX: This could be optimized slightly by not calling - _compute_face again but rather translating the relevant - fields from start. */ - _compute_face (p2, &slope, gstate, end); - - /* XXX: I should really check the return value of the - move_to/line_to functions here to catch out of memory - conditions. But since that would be ugly, I'd prefer to add a - status flag to the polygon object that I could check only once - at then end of this sequence, (like we do with cairo_t - already). */ - _cairo_polygon_init (&polygon); - _cairo_polygon_move_to (&polygon, &start->cw); - _cairo_polygon_line_to (&polygon, &start->ccw); - _cairo_polygon_line_to (&polygon, &end->ccw); - _cairo_polygon_line_to (&polygon, &end->cw); - _cairo_polygon_close (&polygon); - - /* XXX: We can't use tessellate_rectangle as the matrix may have - skewed this into a non-rectangular shape. Perhaps it would be - worth checking the matrix for skew so that the common case - could use the faster tessellate_rectangle rather than - tessellate_polygon? */ - status = _cairo_traps_tessellate_polygon (stroker->traps, - &polygon, CAIRO_FILL_RULE_WINDING); - - _cairo_polygon_fini (&polygon); - - return status; -} - -static cairo_status_t -_cairo_stroker_move_to (void *closure, cairo_point_t *point) -{ - cairo_stroker_t *stroker = closure; - - stroker->first_point = *point; - stroker->current_point = *point; - stroker->has_current_point = 1; - - stroker->has_first_face = 0; - stroker->has_current_face = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_stroker_line_to (void *closure, cairo_point_t *point) -{ - cairo_status_t status; - cairo_stroker_t *stroker = closure; - cairo_stroke_face_t start, end; - cairo_point_t *p1 = &stroker->current_point; - cairo_point_t *p2 = point; - - if (!stroker->has_current_point) - return _cairo_stroker_move_to (stroker, point); - - if (p1->x == p2->x && p1->y == p2->y) { - /* XXX: Need to rethink how this case should be handled, (both - here and in cairo_stroker_add_sub_edge and in _compute_face). The - key behavior is that degenerate paths should draw as much - as possible. */ - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_stroker_add_sub_edge (stroker, p1, p2, &start, &end); - if (status) - return status; - - if (stroker->has_current_face) { - status = _cairo_stroker_join (stroker, &stroker->current_face, &start); - if (status) - return status; - } else { - if (!stroker->has_first_face) { - stroker->first_face = start; - stroker->has_first_face = 1; - } - } - stroker->current_face = end; - stroker->has_current_face = 1; - - stroker->current_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -/* - * Dashed lines. Cap each dash end, join around turns when on - */ -static cairo_status_t -_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_stroker_t *stroker = closure; - cairo_gstate_t *gstate = stroker->gstate; - double mag, remain, tmp; - double dx, dy; - double dx2, dy2; - cairo_point_t fd1, fd2; - int first = 1; - cairo_stroke_face_t sub_start, sub_end; - cairo_point_t *p1 = &stroker->current_point; - cairo_point_t *p2 = point; - - if (!stroker->has_current_point) - return _cairo_stroker_move_to (stroker, point); - - dx = _cairo_fixed_to_double (p2->x - p1->x); - dy = _cairo_fixed_to_double (p2->y - p1->y); - - cairo_matrix_transform_distance (&gstate->ctm_inverse, &dx, &dy); - - mag = sqrt (dx *dx + dy * dy); - remain = mag; - fd1 = *p1; - while (remain) { - tmp = stroker->dash_remain; - if (tmp > remain) - tmp = remain; - remain -= tmp; - dx2 = dx * (mag - remain)/mag; - dy2 = dy * (mag - remain)/mag; - cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); - fd2.x = _cairo_fixed_from_double (dx2); - fd2.y = _cairo_fixed_from_double (dy2); - fd2.x += p1->x; - fd2.y += p1->y; - /* - * XXX simplify this case analysis - */ - if (stroker->dash_on) { - status = _cairo_stroker_add_sub_edge (stroker, &fd1, &fd2, &sub_start, &sub_end); - if (status) - return status; - if (!first) { - /* - * Not first dash in this segment, cap start - */ - status = _cairo_stroker_cap (stroker, &sub_start); - if (status) - return status; - } else { - /* - * First in this segment, join to any current_face, else - * if at start of sub-path, mark position, else - * cap - */ - if (stroker->has_current_face) { - status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); - if (status) - return status; - } else { - if (!stroker->has_first_face) { - stroker->first_face = sub_start; - stroker->has_first_face = 1; - } else { - status = _cairo_stroker_cap (stroker, &sub_start); - if (status) - return status; - } - } - } - if (remain) { - /* - * Cap if not at end of segment - */ - status = _cairo_stroker_cap (stroker, &sub_end); - if (status) - return status; - } else { - /* - * Mark previous line face and fix up next time - * through - */ - stroker->current_face = sub_end; - stroker->has_current_face = 1; - } - } else { - /* - * If starting with off dash, check previous face - * and cap if necessary - */ - if (first) { - if (stroker->has_current_face) { - status = _cairo_stroker_cap (stroker, &stroker->current_face); - if (status) - return status; - } - } - if (!remain) - stroker->has_current_face = 0; - } - _cairo_stroker_step_dash (stroker, tmp); - fd1 = fd2; - first = 0; - } - - stroker->current_point = *point; - - return status; -} - -static cairo_status_t -_cairo_stroker_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_stroker_t *stroker = closure; - cairo_gstate_t *gstate = stroker->gstate; - cairo_spline_t spline; - cairo_pen_t pen; - cairo_stroke_face_t start, end; - cairo_point_t extra_points[4]; - cairo_point_t *a = &stroker->current_point; - - status = _cairo_spline_init (&spline, a, b, c, d); - if (status == CAIRO_INT_STATUS_DEGENERATE) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pen_init_copy (&pen, &gstate->pen_regular); - if (status) - goto CLEANUP_SPLINE; - - _compute_face (a, &spline.initial_slope, gstate, &start); - _compute_face (d, &spline.final_slope, gstate, &end); - - if (stroker->has_current_face) { - status = _cairo_stroker_join (stroker, &stroker->current_face, &start); - if (status) - return status; - } else { - if (!stroker->has_first_face) { - stroker->first_face = start; - stroker->has_first_face = 1; - } - } - stroker->current_face = end; - stroker->has_current_face = 1; - - extra_points[0] = start.cw; - extra_points[0].x -= start.point.x; - extra_points[0].y -= start.point.y; - extra_points[1] = start.ccw; - extra_points[1].x -= start.point.x; - extra_points[1].y -= start.point.y; - extra_points[2] = end.cw; - extra_points[2].x -= end.point.x; - extra_points[2].y -= end.point.y; - extra_points[3] = end.ccw; - extra_points[3].x -= end.point.x; - extra_points[3].y -= end.point.y; - - status = _cairo_pen_add_points (&pen, extra_points, 4); - if (status) - goto CLEANUP_PEN; - - status = _cairo_pen_stroke_spline (&pen, &spline, gstate->tolerance, stroker->traps); - if (status) - goto CLEANUP_PEN; - - CLEANUP_PEN: - _cairo_pen_fini (&pen); - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); - - stroker->current_point = *d; - - return status; -} - -static cairo_status_t -_cairo_stroker_close_path (void *closure) -{ - cairo_status_t status; - cairo_stroker_t *stroker = closure; - - if (stroker->has_current_point) { - if (stroker->dashed) - status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); - else - status = _cairo_stroker_line_to (stroker, &stroker->first_point); - if (status) - return status; - } - - if (stroker->has_first_face && stroker->has_current_face) { - status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); - if (status) - return status; - } - - stroker->has_first_face = 0; - stroker->has_current_face = 0; - stroker->has_current_point = 0; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_path_stroke_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_stroker_t stroker; - - _cairo_stroker_init (&stroker, gstate, traps); - - if (gstate->dash) - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - _cairo_stroker_line_to_dashed, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); - else - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, - _cairo_stroker_line_to, - _cairo_stroker_curve_to, - _cairo_stroker_close_path, - &stroker); - if (status) - goto BAIL; - - if (stroker.has_first_face) { - cairo_point_t t; - /* The initial cap needs an outward facing vector. Reverse everything */ - stroker.first_face.usr_vector.x = -stroker.first_face.usr_vector.x; - stroker.first_face.usr_vector.y = -stroker.first_face.usr_vector.y; - stroker.first_face.dev_vector.dx = -stroker.first_face.dev_vector.dx; - stroker.first_face.dev_vector.dy = -stroker.first_face.dev_vector.dy; - t = stroker.first_face.cw; - stroker.first_face.cw = stroker.first_face.ccw; - stroker.first_face.ccw = t; - status = _cairo_stroker_cap (&stroker, &stroker.first_face); - if (status) - goto BAIL; - } - - if (stroker.has_current_face) { - status = _cairo_stroker_cap (&stroker, &stroker.current_face); - if (status) - goto BAIL; - } - -BAIL: - _cairo_stroker_fini (&stroker); - - return status; -} diff --git a/src/cairo_pattern.c b/src/cairo_pattern.c deleted file mode 100644 index 283c36dbd..000000000 --- a/src/cairo_pattern.c +++ /dev/null @@ -1,1325 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 David Reveman - * - * Permission to use, copy, modify, distribute, and sell this software - * and its documentation for any purpose is hereby granted without - * fee, provided that the above copyright notice appear in all copies - * and that both that copyright notice and this permission notice - * appear in supporting documentation, and that the name of David - * Reveman not be used in advertising or publicity pertaining to - * distribution of the software without specific, written prior - * permission. David Reveman makes no representations about the - * suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author: David Reveman <davidr@novell.com> - */ - -#include "cairoint.h" - -typedef void (*cairo_shader_function_t) (unsigned char *color0, - unsigned char *color1, - cairo_fixed_t factor, - uint32_t *pixel); - -typedef struct _cairo_shader_color_stop { - cairo_fixed_t offset; - cairo_fixed_48_16_t scale; - int id; - unsigned char color_char[4]; -} cairo_shader_color_stop_t; - -typedef struct _cairo_shader_op { - cairo_shader_color_stop_t *stops; - int n_stops; - cairo_extend_t extend; - cairo_shader_function_t shader_function; -} cairo_shader_op_t; - -#define MULTIPLY_COLORCOMP(c1, c2) \ - ((unsigned char) \ - ((((unsigned char) (c1)) * (int) ((unsigned char) (c2))) / 0xff)) - -static void -_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) -{ - pattern->type = type; - pattern->ref_count = 1; - pattern->extend = CAIRO_EXTEND_DEFAULT; - pattern->filter = CAIRO_FILTER_DEFAULT; - pattern->alpha = 1.0; - - _cairo_matrix_init (&pattern->matrix); -} - -static cairo_status_t -_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, - cairo_gradient_pattern_t *other) -{ - if (other->base.type == CAIRO_PATTERN_LINEAR) - { - cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; - cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; - - *dst = *src; - } - else - { - cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; - cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; - - *dst = *src; - } - - if (other->n_stops) - { - pattern->stops = malloc (other->n_stops * sizeof (cairo_color_stop_t)); - if (!pattern->stops) - return CAIRO_STATUS_NO_MEMORY; - - memcpy (pattern->stops, other->stops, - other->n_stops * sizeof (cairo_color_stop_t)); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) -{ - switch (other->type) { - case CAIRO_PATTERN_SOLID: { - cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; - cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; - - *dst = *src; - } break; - case CAIRO_PATTERN_SURFACE: { - cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; - cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; - - *dst = *src; - cairo_surface_reference (dst->surface); - } break; - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; - cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; - cairo_status_t status; - - status = _cairo_gradient_pattern_init_copy (dst, src); - if (status) - return status; - } break; - } - - pattern->ref_count = 1; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pattern_fini (cairo_pattern_t *pattern) -{ - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: - break; - case CAIRO_PATTERN_SURFACE: { - cairo_surface_pattern_t *fini = (cairo_surface_pattern_t *) pattern; - - cairo_surface_destroy (fini->surface); - } break; - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_gradient_pattern_t *fini = (cairo_gradient_pattern_t *) pattern; - - if (fini->n_stops) - free (fini->stops); - } break; - } -} - -void -_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, - double red, - double green, - double blue) -{ - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID); - - pattern->red = red; - pattern->green = green; - pattern->blue = blue; -} - -void -_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, - cairo_surface_t *surface) -{ - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE); - - pattern->surface = surface; - cairo_surface_reference (surface); -} - -static void -_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, - cairo_pattern_type_t type) -{ - _cairo_pattern_init (&pattern->base, type); - - pattern->stops = 0; - pattern->n_stops = 0; -} - -void -_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, - double x0, double y0, double x1, double y1) -{ - _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_LINEAR); - - pattern->point0.x = x0; - pattern->point0.y = y0; - pattern->point1.x = x1; - pattern->point1.y = y1; -} - -void -_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, - double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1) -{ - _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_RADIAL); - - pattern->center0.x = cx0; - pattern->center0.y = cy0; - pattern->radius0 = fabs (radius0); - pattern->center1.x = cx1; - pattern->center1.y = cy1; - pattern->radius1 = fabs (radius1); -} - -cairo_pattern_t * -_cairo_pattern_create_solid (double red, double green, double blue) -{ - cairo_solid_pattern_t *pattern; - - pattern = malloc (sizeof (cairo_solid_pattern_t)); - if (pattern == NULL) - return NULL; - - _cairo_pattern_init_solid (pattern, red, green, blue); - - return &pattern->base; -} - -cairo_pattern_t * -cairo_pattern_create_for_surface (cairo_surface_t *surface) -{ - cairo_surface_pattern_t *pattern; - - pattern = malloc (sizeof (cairo_surface_pattern_t)); - if (pattern == NULL) - return NULL; - - _cairo_pattern_init_for_surface (pattern, surface); - - /* this will go away when we completely remove the surface attributes */ - if (surface->repeat) - pattern->base.extend = CAIRO_EXTEND_REPEAT; - else - pattern->base.extend = CAIRO_EXTEND_DEFAULT; - - return &pattern->base; -} - -cairo_pattern_t * -cairo_pattern_create_linear (double x0, double y0, double x1, double y1) -{ - cairo_linear_pattern_t *pattern; - - pattern = malloc (sizeof (cairo_linear_pattern_t)); - if (pattern == NULL) - return NULL; - - _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); - - return &pattern->base.base; -} - -cairo_pattern_t * -cairo_pattern_create_radial (double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1) -{ - cairo_radial_pattern_t *pattern; - - pattern = malloc (sizeof (cairo_radial_pattern_t)); - if (pattern == NULL) - return NULL; - - _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); - - return &pattern->base.base; -} - -void -cairo_pattern_reference (cairo_pattern_t *pattern) -{ - if (pattern == NULL) - return; - - pattern->ref_count++; -} - -void -cairo_pattern_destroy (cairo_pattern_t *pattern) -{ - if (pattern == NULL) - return; - - pattern->ref_count--; - if (pattern->ref_count) - return; - - _cairo_pattern_fini (pattern); - free (pattern); -} - -static cairo_status_t -_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, - double offset, - double red, - double green, - double blue, - double alpha) -{ - cairo_color_stop_t *stop; - - pattern->n_stops++; - pattern->stops = realloc (pattern->stops, - pattern->n_stops * sizeof (cairo_color_stop_t)); - if (pattern->stops == NULL) { - pattern->n_stops = 0; - - return CAIRO_STATUS_NO_MEMORY; - } - - stop = &pattern->stops[pattern->n_stops - 1]; - - stop->offset = _cairo_fixed_from_double (offset); - - _cairo_color_init (&stop->color); - _cairo_color_set_rgb (&stop->color, red, green, blue); - _cairo_color_set_alpha (&stop->color, alpha); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -cairo_pattern_add_color_stop (cairo_pattern_t *pattern, - double offset, - double red, - double green, - double blue, - double alpha) -{ - if (pattern->type != CAIRO_PATTERN_LINEAR && - pattern->type != CAIRO_PATTERN_RADIAL) - { - /* XXX: CAIRO_STATUS_INVALID_PATTERN? */ - return CAIRO_STATUS_SUCCESS; - } - - _cairo_restrict_value (&offset, 0.0, 1.0); - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); - _cairo_restrict_value (&alpha, 0.0, 1.0); - - return _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, - offset, - red, green, blue, - alpha); -} - -cairo_status_t -cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) -{ - return cairo_matrix_copy (&pattern->matrix, matrix); -} - -cairo_status_t -cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) -{ - return cairo_matrix_copy (matrix, &pattern->matrix); -} - -cairo_status_t -cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) -{ - pattern->filter = filter; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_filter_t -cairo_pattern_get_filter (cairo_pattern_t *pattern) -{ - return pattern->filter; -} - -cairo_status_t -cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) -{ - pattern->extend = extend; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_extend_t -cairo_pattern_get_extend (cairo_pattern_t *pattern) -{ - return pattern->extend; -} - -cairo_status_t -_cairo_pattern_get_rgb (cairo_pattern_t *pattern, - double *red, - double *green, - double *blue) -{ - - if (pattern->type == CAIRO_PATTERN_SOLID) - { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - - *red = solid->red; - *green = solid->green; - *blue = solid->blue; - } else - *red = *green = *blue = 1.0; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha) -{ - pattern->alpha = alpha; -} - -void -_cairo_pattern_transform (cairo_pattern_t *pattern, - cairo_matrix_t *ctm_inverse) -{ - cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); -} - -#define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \ - ((factor < 32768)? c1: c2) - -static void -_cairo_pattern_shader_nearest (unsigned char *color0, - unsigned char *color1, - cairo_fixed_t factor, - uint32_t *pixel) -{ - *pixel = - ((INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor) << 24) | - (INTERPOLATE_COLOR_NEAREST (color0[0], color1[0], factor) << 16) | - (INTERPOLATE_COLOR_NEAREST (color0[1], color1[1], factor) << 8) | - (INTERPOLATE_COLOR_NEAREST (color0[2], color1[2], factor) << 0)); -} - -#undef INTERPOLATE_COLOR_NEAREST - -#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \ - (((c2 * factor) + (c1 * (65536 - factor))) / 65536) - -static void -_cairo_pattern_shader_linear (unsigned char *color0, - unsigned char *color1, - cairo_fixed_t factor, - uint32_t *pixel) -{ - *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) | - (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) | - (INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) | - (INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0)); -} - -#define E_MINUS_ONE 1.7182818284590452354 - -static void -_cairo_pattern_shader_gaussian (unsigned char *color0, - unsigned char *color1, - cairo_fixed_t factor, - uint32_t *pixel) -{ - double f = ((double) factor) / 65536.0; - - factor = (cairo_fixed_t) (((exp (f * f) - 1.0) / E_MINUS_ONE) * 65536); - - *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) | - (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) | - (INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) | - (INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0)); -} - -#undef INTERPOLATE_COLOR_LINEAR - -static int -_cairo_shader_color_stop_compare (const void *elem1, const void *elem2) -{ - cairo_shader_color_stop_t *s1 = (cairo_shader_color_stop_t *) elem1; - cairo_shader_color_stop_t *s2 = (cairo_shader_color_stop_t *) elem2; - - return - (s1->offset == s2->offset) ? - /* equal offsets, sort on id */ - ((s1->id < s2->id) ? -1 : 1) : - /* sort on offset */ - ((s1->offset < s2->offset) ? -1 : 1); -} - -static cairo_status_t -_cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern, - cairo_shader_op_t *op) -{ - int i; - - op->stops = malloc (pattern->n_stops * sizeof (cairo_shader_color_stop_t)); - if (!op->stops) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < pattern->n_stops; i++) - { - op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff; - op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff; - op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff; - op->stops[i].color_char[3] = pattern->stops[i].color.alpha * - pattern->base.alpha * 0xff; - op->stops[i].offset = pattern->stops[i].offset; - op->stops[i].id = i; - } - - /* sort stops in ascending order */ - qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t), - _cairo_shader_color_stop_compare); - - for (i = 0; i < pattern->n_stops - 1; i++) - { - op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset; - if (op->stops[i + 1].scale == 65536) - op->stops[i + 1].scale = 0; - } - - op->n_stops = pattern->n_stops; - op->extend = pattern->base.extend; - - /* XXX: this is wrong, the filter should not be used for selecting - color stop interpolation function. function should always be 'linear' - and filter should be used for computing pixels. */ - switch (pattern->base.filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - op->shader_function = _cairo_pattern_shader_nearest; - break; - case CAIRO_FILTER_GAUSSIAN: - op->shader_function = _cairo_pattern_shader_gaussian; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - op->shader_function = _cairo_pattern_shader_linear; - break; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_pattern_shader_fini (cairo_shader_op_t *op) -{ - if (op->stops) - free (op->stops); -} - -/* Find two color stops bounding the given offset. If the given offset - * is before the first or after the last stop offset, the nearest - * offset is returned twice. - */ -static void -_cairo_shader_op_find_color_stops (cairo_shader_op_t *op, - cairo_fixed_t offset, - cairo_shader_color_stop_t *stops[2]) -{ - int i; - - /* Before first stop. */ - if (offset <= op->stops[0].offset) { - stops[0] = &op->stops[0]; - stops[1] = &op->stops[0]; - return; - } - - /* Between two stops. */ - for (i = 0; i < op->n_stops - 1; i++) { - if (offset <= op->stops[i + 1].offset) { - stops[0] = &op->stops[i]; - stops[1] = &op->stops[i + 1]; - return; - } - } - - /* After last stop. */ - stops[0] = &op->stops[op->n_stops - 1]; - stops[1] = &op->stops[op->n_stops - 1]; -} - -static void -_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, - cairo_fixed_t factor, - uint32_t *pixel) -{ - cairo_shader_color_stop_t *stops[2]; - - switch (op->extend) { - case CAIRO_EXTEND_REPEAT: - factor -= factor & 0xffff0000; - break; - case CAIRO_EXTEND_REFLECT: - if (factor < 0 || factor > 65536) { - if ((factor >> 16) % 2) - factor = 65536 - (factor - (factor & 0xffff0000)); - else - factor -= factor & 0xffff0000; - } - break; - case CAIRO_EXTEND_NONE: - break; - } - - _cairo_shader_op_find_color_stops (op, factor, stops); - - /* take offset as new 0 of coordinate system */ - factor -= stops[0]->offset; - - /* difference between two offsets == 0, abrubt change */ - if (stops[1]->scale) - factor = ((cairo_fixed_48_16_t) factor << 16) / - stops[1]->scale; - - op->shader_function (stops[0]->color_char, - stops[1]->color_char, - factor, pixel); - - /* multiply alpha */ - if (((unsigned char) (*pixel >> 24)) != 0xff) { - *pixel = (*pixel & 0xff000000) | - (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) | - (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) | - (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0); - } -} - -static cairo_status_t -_cairo_image_data_set_linear (cairo_linear_pattern_t *pattern, - double offset_x, - double offset_y, - uint32_t *pixels, - int width, - int height) -{ - int x, y; - cairo_point_double_t point0, point1; - double a, b, c, d, tx, ty; - double scale, start, dx, dy, factor; - cairo_shader_op_t op; - cairo_status_t status; - - status = _cairo_pattern_shader_init (&pattern->base, &op); - if (status) - return status; - - /* We compute the position in the linear gradient for - * a point q as: - * - * [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2 - * - * The computation is done in pattern space. The - * calculation could be heavily optimized by using the - * fact that 'factor' increases linearly in both - * directions. - */ - point0.x = pattern->point0.x; - point0.y = pattern->point0.y; - point1.x = pattern->point1.x; - point1.y = pattern->point1.y; - - cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); - - dx = point1.x - point0.x; - dy = point1.y - point0.y; - scale = dx * dx + dy * dy; - scale = (scale) ? 1.0 / scale : 1.0; - - start = dx * point0.x + dy * point0.y; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - double qx_device = x + offset_x; - double qy_device = y + offset_y; - - /* transform fragment into pattern space */ - double qx = a * qx_device + c * qy_device + tx; - double qy = b * qx_device + d * qy_device + ty; - - factor = ((dx * qx + dy * qy) - start) * scale; - - _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++); - } - } - - _cairo_pattern_shader_fini (&op); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, - double offset_x, - double offset_y, - int width, - int height, - cairo_bool_t *is_horizontal, - cairo_bool_t *is_vertical) -{ - cairo_point_double_t point0, point1; - double a, b, c, d, tx, ty; - double scale, start, dx, dy; - cairo_fixed_t factors[3]; - int i; - - /* To classidy a pattern as horizontal or vertical, we first - * compute the (fixed point) factors at the corners of the - * pattern. We actually only need 3/4 corners, so we skip the - * fourth. - */ - point0.x = pattern->point0.x; - point0.y = pattern->point0.y; - point1.x = pattern->point1.x; - point1.y = pattern->point1.y; - - cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); - - dx = point1.x - point0.x; - dy = point1.y - point0.y; - scale = dx * dx + dy * dy; - scale = (scale) ? 1.0 / scale : 1.0; - - start = dx * point0.x + dy * point0.y; - - for (i = 0; i < 3; i++) { - double qx_device = (i % 2) * (width - 1) + offset_x; - double qy_device = (i / 2) * (height - 1) + offset_y; - - /* transform fragment into pattern space */ - double qx = a * qx_device + c * qy_device + tx; - double qy = b * qx_device + d * qy_device + ty; - - factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale); - } - - /* We consider a pattern to be vertical if the fixed point factor - * at the two upper corners is the same. We could accept a small - * change, but determining what change is acceptable would require - * sorting the stops in the pattern and looking at the differences. - * - * Horizontal works the same way with the two left corners. - */ - - *is_vertical = factors[1] == factors[0]; - *is_horizontal = factors[2] == factors[0]; -} - -static cairo_status_t -_cairo_image_data_set_radial (cairo_radial_pattern_t *pattern, - double offset_x, - double offset_y, - uint32_t *pixels, - int width, - int height) -{ - int x, y, aligned_circles; - cairo_point_double_t c0, c1; - double px, py, ex, ey; - double a, b, c, d, tx, ty; - double r0, r1, c0_e_x, c0_e_y, c0_e, c1_e_x, c1_e_y, c1_e, - c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2, - denumerator, fraction, factor; - cairo_shader_op_t op; - cairo_status_t status; - - status = _cairo_pattern_shader_init (&pattern->base, &op); - if (status) - return status; - - c0.x = pattern->center0.x; - c0.y = pattern->center0.y; - r0 = pattern->radius0; - c1.x = pattern->center1.x; - c1.y = pattern->center1.y; - r1 = pattern->radius1; - - if (c0.x != c1.x || c0.y != c1.y) { - aligned_circles = 0; - c0_c1_x = c1.x - c0.x; - c0_c1_y = c1.y - c0.y; - c0_c1 = sqrt (c0_c1_x * c0_c1_x + c0_c1_y * c0_c1_y); - r1_2 = r1 * r1; - } else { - aligned_circles = 1; - r1 = 1.0 / (r1 - r0); - r1_2 = c0_c1 = 0.0; /* shut up compiler */ - } - - cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - px = x + offset_x; - py = y + offset_y; - - /* transform fragment */ - ex = a * px + c * py + tx; - ey = b * px + d * py + ty; - - if (aligned_circles) { - ex = ex - c1.x; - ey = ey - c1.y; - - factor = (sqrt (ex * ex + ey * ey) - r0) * r1; - } else { - /* - y (ex, ey) - c0 -------------------+---------- x - \ | __-- - \ | __-- - \ | __-- - \ | __-- r1 - \ | __-- - c1 -- - - We need to calulate distance c0->x; the distance from - the inner circle center c0, through fragment position - (ex, ey) to point x where it crosses the outer circle. - - From points c0, c1 and (ex, ey) we get angle C0. With - angle C0 we calculate distance c1->y and c0->y and by - knowing c1->y and r1, we also know y->x. Adding y->x to - c0->y gives us c0->x. The gradient offset can then be - calculated as: - - offset = (c0->e - r0) / (c0->x - r0) - - */ - - c0_e_x = ex - c0.x; - c0_e_y = ey - c0.y; - c0_e = sqrt (c0_e_x * c0_e_x + c0_e_y * c0_e_y); - - c1_e_x = ex - c1.x; - c1_e_y = ey - c1.y; - c1_e = sqrt (c1_e_x * c1_e_x + c1_e_y * c1_e_y); - - denumerator = -2.0 * c0_e * c0_c1; - - if (denumerator != 0.0) { - fraction = (c1_e * c1_e - c0_e * c0_e - c0_c1 * c0_c1) / - denumerator; - - if (fraction > 1.0) - fraction = 1.0; - else if (fraction < -1.0) - fraction = -1.0; - - angle_c0 = acos (fraction); - - c0_y = cos (angle_c0) * c0_c1; - c1_y = sin (angle_c0) * c0_c1; - - y_x = sqrt (r1_2 - c1_y * c1_y); - c0_x = y_x + c0_y; - - factor = (c0_e - r0) / (c0_x - r0); - } else - factor = -r0; - } - - _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++); - } - } - - _cairo_pattern_shader_fini (&op); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attr) -{ - cairo_image_surface_t *image; - cairo_status_t status; - uint32_t *data; - cairo_bool_t repeat = FALSE; - - if (pattern->base.type == CAIRO_PATTERN_LINEAR) { - cairo_bool_t is_horizontal; - cairo_bool_t is_vertical; - - _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern, - x, y, width, height, - &is_horizontal, &is_vertical); - if (is_horizontal) { - height = 1; - repeat = TRUE; - } - if (is_vertical) { - width = 1; - repeat = TRUE; - } - } - - data = malloc (width * height * 4); - if (!data) - return CAIRO_STATUS_NO_MEMORY; - - if (pattern->base.type == CAIRO_PATTERN_LINEAR) - { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - - status = _cairo_image_data_set_linear (linear, x, y, data, - width, height); - } - else - { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - - status = _cairo_image_data_set_radial (radial, x, y, data, - width, height); - } - - if (status) { - free (data); - return status; - } - - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data ((char *) data, - CAIRO_FORMAT_ARGB32, - width, height, - width * 4); - - if (image == NULL) { - free (data); - return CAIRO_STATUS_NO_MEMORY; - } - - _cairo_image_surface_assume_ownership_of_data (image); - - status = _cairo_surface_clone_similar (dst, &image->base, out); - - cairo_surface_destroy (&image->base); - - attr->x_offset = -x; - attr->y_offset = -y; - cairo_matrix_set_identity (&attr->matrix); - attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; - attr->filter = CAIRO_FILTER_NEAREST; - attr->acquired = FALSE; - - return status; -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attribs) -{ - cairo_color_t color; - - _cairo_color_init (&color); - _cairo_color_set_rgb (&color, pattern->red, pattern->green, pattern->blue); - _cairo_color_set_alpha (&color, pattern->base.alpha); - - *out = _cairo_surface_create_similar_solid (dst, - CAIRO_FORMAT_ARGB32, - 1, 1, - &color); - - if (*out == NULL) - return CAIRO_STATUS_NO_MEMORY; - - attribs->x_offset = attribs->y_offset = 0; - cairo_matrix_set_identity (&attribs->matrix); - attribs->extend = CAIRO_EXTEND_REPEAT; - attribs->filter = CAIRO_FILTER_NEAREST; - attribs->acquired = FALSE; - - return CAIRO_STATUS_SUCCESS; -} - - -/** - * _cairo_pattern_is_opaque - * - * Convenience function to determine whether a pattern has an opaque - * alpha value. This is done by testing whether the pattern's alpha - * value when converted to a byte is 255, so if a backend actually - * supported deep alpha channels this function might not do the right - * thing. - * - * Note that for a gradient or surface pattern, the overall resulting - * alpha for the pattern can be non-opaque even this function returns - * %TRUE, since the resulting alpha is the multiplication of the - * alpha of the gradient or surface with the pattern's alpha. In - * the future, alpha will be moved from the base pattern to the - * solid pattern subtype, at which point this function should - * probably be renamed to _cairo_pattern_is_opaque_solid() - * - * Return value: %TRUE if the pattern is opaque - **/ -cairo_bool_t -_cairo_pattern_is_opaque (cairo_pattern_t *pattern) -{ - return (pattern->alpha >= ((double)0xff00 / (double)0xffff)); -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attr) -{ - cairo_int_status_t status; - - attr->acquired = FALSE; - - /* handle pattern opacity */ - if (!_cairo_pattern_is_opaque (&pattern->base)) - { - cairo_surface_pattern_t tmp; - cairo_color_t color; - - _cairo_color_init (&color); - _cairo_color_set_alpha (&color, pattern->base.alpha); - - *out = _cairo_surface_create_similar_solid (dst, - CAIRO_FORMAT_ARGB32, - width, height, - &color); - if (*out == NULL) - return CAIRO_STATUS_NO_MEMORY; - - status = _cairo_pattern_init_copy (&tmp.base, &pattern->base); - if (CAIRO_OK (status)) - { - tmp.base.alpha = 1.0; - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &tmp.base, - NULL, - *out, - x, y, 0, 0, 0, 0, - width, height); - - _cairo_pattern_fini (&tmp.base); - } - - if (status) { - cairo_surface_destroy (*out); - return status; - } - - attr->x_offset = -x; - attr->y_offset = -y; - attr->extend = CAIRO_EXTEND_NONE; - attr->filter = CAIRO_FILTER_NEAREST; - - cairo_matrix_set_identity (&attr->matrix); - } - else - { - int tx, ty; - - if (_cairo_surface_is_image (dst)) - { - cairo_image_surface_t *image; - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &attr->extra); - if (CAIRO_OK (status)) - *out = &image->base; - - attr->acquired = TRUE; - } - else - status = _cairo_surface_clone_similar (dst, pattern->surface, out); - - attr->extend = pattern->base.extend; - attr->filter = pattern->base.filter; - if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, - &tx, &ty)) - { - cairo_matrix_set_identity (&attr->matrix); - attr->x_offset = tx; - attr->y_offset = ty; - } - else - { - attr->matrix = pattern->base.matrix; - attr->x_offset = attr->y_offset = 0; - } - } - - return status; -} - -/** - * _cairo_pattern_acquire_surface: - * @pattern: a #cairo_pattern_t - * @dst: destination surface - * @x: X coordinate in source corresponding to left side of destination area - * @y: Y coordinate in source corresponding to top side of destination area - * @width: width of destination area - * @height: height of destination area - * @surface_out: location to store a pointer to a surface - * @attributes: surface attributes that destination backend should apply to - * the returned surface - * - * A convenience function to obtain a surface to use as the source for - * drawing on @dst. - * - * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. - **/ -cairo_int_status_t -_cairo_pattern_acquire_surface (cairo_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **surface_out, - cairo_surface_attributes_t *attributes) -{ - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: { - cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern; - - return _cairo_pattern_acquire_surface_for_solid (src, dst, - x, y, width, height, - surface_out, - attributes); - } break; - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern; - - /* fast path for gradients with less than 2 color stops */ - if (src->n_stops < 2) - { - cairo_solid_pattern_t solid; - - if (src->n_stops) - { - _cairo_pattern_init_solid (&solid, - src->stops->color.red, - src->stops->color.green, - src->stops->color.blue); - _cairo_pattern_set_alpha (&solid.base, - src->stops->color.alpha); - } - else - { - _cairo_pattern_init_solid (&solid, 0.0, 0.0, 0.0); - _cairo_pattern_set_alpha (&solid.base, 0.0); - } - - return _cairo_pattern_acquire_surface_for_solid (&solid, dst, - x, y, - width, height, - surface_out, - attributes); - } - else - return _cairo_pattern_acquire_surface_for_gradient (src, dst, - x, y, - width, height, - surface_out, - attributes); - } break; - case CAIRO_PATTERN_SURFACE: { - cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; - - return _cairo_pattern_acquire_surface_for_surface (src, dst, - x, y, width, height, - surface_out, - attributes); - } break; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -/** - * _cairo_pattern_release_surface: - * @pattern: a #cairo_pattern_t - * @info: pointer to #cairo_surface_attributes_t filled in by - * _cairo_pattern_acquire_surface - * - * Releases resources obtained by _cairo_pattern_acquire_surface. - **/ -void -_cairo_pattern_release_surface (cairo_surface_t *dst, - cairo_surface_t *surface, - cairo_surface_attributes_t *attributes) -{ - if (attributes->acquired) - _cairo_surface_release_source_image (dst, - (cairo_image_surface_t *) surface, - attributes->extra); - else - cairo_surface_destroy (surface); -} - -cairo_int_status_t -_cairo_pattern_acquire_surfaces (cairo_pattern_t *src, - cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_surface_t **src_out, - cairo_surface_t **mask_out, - cairo_surface_attributes_t *src_attributes, - cairo_surface_attributes_t *mask_attributes) -{ - cairo_int_status_t status; - - cairo_pattern_union_t tmp; - cairo_bool_t src_opaque, mask_opaque; - double src_alpha, mask_alpha; - - src_opaque = _cairo_pattern_is_opaque (src); - mask_opaque = !mask || _cairo_pattern_is_opaque (mask); - - /* For surface patterns, we move any translucency from src->alpha - * to mask->alpha so we can use the source unchanged. Otherwise we - * move the translucency from mask->alpha to src->alpha so that - * we can drop the mask if possible. - */ - if (src->type == CAIRO_PATTERN_SURFACE) - { - if (mask) { - mask_opaque = mask_opaque && src_opaque; - mask_alpha = mask->alpha * src->alpha; - } else { - mask_opaque = src_opaque; - mask_alpha = src->alpha; - } - - src_alpha = 1.0; - src_opaque = TRUE; - } - else - { - if (mask) - { - src_opaque = mask_opaque && src_opaque; - src_alpha = mask->alpha * src->alpha; - /* FIXME: This needs changing when we support RENDER - * style 4-channel masks. - */ - if (mask->type == CAIRO_PATTERN_SOLID) - mask = NULL; - } else - src_alpha = src->alpha; - - mask_alpha = 1.0; - mask_opaque = TRUE; - } - - _cairo_pattern_init_copy (&tmp.base, src); - _cairo_pattern_set_alpha (&tmp.base, src_alpha); - - status = _cairo_pattern_acquire_surface (&tmp.base, dst, - src_x, src_y, - width, height, - src_out, src_attributes); - - _cairo_pattern_fini (&tmp.base); - - if (status) - return status; - - if (mask || !mask_opaque) - { - if (mask) - _cairo_pattern_init_copy (&tmp.base, mask); - else - _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); - - _cairo_pattern_set_alpha (&tmp.base, mask_alpha); - - status = _cairo_pattern_acquire_surface (&tmp.base, dst, - mask_x, mask_y, - width, height, - mask_out, mask_attributes); - - _cairo_pattern_fini (&tmp.base); - - if (status) - { - _cairo_pattern_release_surface (dst, *src_out, src_attributes); - return status; - } - } - else - { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo_pdf_surface.c b/src/cairo_pdf_surface.c deleted file mode 100644 index fee918355..000000000 --- a/src/cairo_pdf_surface.c +++ /dev/null @@ -1,2222 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 Red Hat, Inc - * - * 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Kristian Høgsberg <krh@redhat.com> - */ - -#include "cairoint.h" -#include "cairo-pdf.h" -/* XXX: Eventually, we need to handle other font backends */ -#include "cairo-ft-private.h" - -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_TRUETYPE_TAGS_H -#include FT_TRUETYPE_TABLES_H - -#include <time.h> -#include <zlib.h> - -/* Issues: - * - * - Why doesn't pages inherit /alpha%d GS dictionaries from the Pages - * object? - * - * - We embed an image in the stream each time it's composited. We - * could add generation counters to surfaces and remember the stream - * ID for a particular generation for a particular surface. - * - * - Multi stop gradients. What are the exponential interpolation - * functions, could they be used for gradients? - * - * - Clipping: must be able to reset clipping - * - * - Images of other formats than 8 bit RGBA. - * - * - Backend specific meta data. - * - * - Surface patterns. - * - * - Alpha channels in gradients. - * - * - Should/does cairo support drawing into a scratch surface and then - * using that as a fill pattern? For this backend, that would involve - * using a tiling pattern (4.6.2). How do you create such a scratch - * surface? cairo_surface_create_similar() ? - * - * - What if you create a similiar surface and does show_page and then - * does show_surface on another surface? - * - * - Output TM so page scales to the right size - PDF default user - * space has 1 unit = 1 / 72 inch. - * - * - Add test case for RGBA images. - * - * - Add test case for RGBA gradients. - * - * - Pattern extend isn't honoured by image backend. - * - * - Coordinate space for create_similar() args? - * - * - Investigate /Matrix entry in content stream dicts for pages - * instead of outputting the cm operator in every page. - */ - -typedef struct ft_subset_glyph ft_subset_glyph_t; -struct ft_subset_glyph { - int parent_index; - unsigned long location; -}; - -typedef struct cairo_pdf_font_backend cairo_pdf_font_backend_t; -struct cairo_pdf_font_backend { - int (*use_glyph) (void *abstract_font, - int glyph); - cairo_status_t (*generate) (void *abstract_font, - const char **data, - unsigned long *length); - void (*destroy) (void *abstract_font); -}; - -typedef struct cairo_pdf_font cairo_pdf_font_t; -struct cairo_pdf_font { - cairo_pdf_font_backend_t *backend; - cairo_unscaled_font_t *unscaled_font; - unsigned int font_id; - char *base_font; - int num_glyphs; - int *widths; - long x_min, y_min, x_max, y_max; - long ascent, descent; -}; - -typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t; -struct cairo_pdf_ft_font { - cairo_pdf_font_t base; - ft_subset_glyph_t *glyphs; - FT_Face face; - unsigned long *checksum_location; - cairo_array_t output; - int *parent_to_subset; - cairo_status_t status; -}; - -typedef struct cairo_pdf_object cairo_pdf_object_t; -typedef struct cairo_pdf_resource cairo_pdf_resource_t; -typedef struct cairo_pdf_stream cairo_pdf_stream_t; -typedef struct cairo_pdf_document cairo_pdf_document_t; -typedef struct cairo_pdf_surface cairo_pdf_surface_t; - -struct cairo_pdf_object { - long offset; -}; - -struct cairo_pdf_resource { - unsigned int id; -}; - -struct cairo_pdf_stream { - unsigned int id; - unsigned int length_id; - long start_offset; -}; - -struct cairo_pdf_document { - FILE *file; - unsigned long refcount; - - double width_inches; - double height_inches; - double x_ppi; - double y_ppi; - - unsigned int next_available_id; - unsigned int pages_id; - - cairo_pdf_stream_t *current_stream; - - cairo_array_t objects; - cairo_array_t pages; - - cairo_array_t fonts; -}; - -struct cairo_pdf_surface { - cairo_surface_t base; - - double width_inches; - double height_inches; - - cairo_pdf_document_t *document; - cairo_pdf_stream_t *current_stream; - - cairo_array_t patterns; - cairo_array_t xobjects; - cairo_array_t streams; - cairo_array_t alphas; - cairo_array_t fonts; -}; - - -static cairo_pdf_document_t * -_cairo_pdf_document_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch); - -static void -_cairo_pdf_document_destroy (cairo_pdf_document_t *document); - -static void -_cairo_pdf_document_reference (cairo_pdf_document_t *document); - -static unsigned int -_cairo_pdf_document_new_object (cairo_pdf_document_t *document); - -static cairo_status_t -_cairo_pdf_document_add_page (cairo_pdf_document_t *document, - cairo_pdf_surface_t *surface); - -static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); - -static cairo_pdf_stream_t * -_cairo_pdf_document_open_stream (cairo_pdf_document_t *document, - const char *extra_entries); -static cairo_surface_t * -_cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, - double width_inches, - double height_inches); -static void -_cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface, - cairo_pdf_stream_t *stream); -static void -_cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface); - -static const cairo_surface_backend_t cairo_pdf_surface_backend; - -/* Truetype font subsetting code */ - -#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) - -#define SFNT_VERSION 0x00010000 - -#ifdef WORDS_BIGENDIAN - -#define cpu_to_be16(v) (v) -#define be16_to_cpu(v) (v) -#define cpu_to_be32(v) (v) -#define be32_to_cpu(v) (v) - -#else - -static inline unsigned short -cpu_to_be16(unsigned short v) -{ - return (v << 8) | (v >> 8); -} - -static inline unsigned short -be16_to_cpu(unsigned short v) -{ - return cpu_to_be16 (v); -} - -static inline unsigned long -cpu_to_be32(unsigned long v) -{ - return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); -} - -static inline unsigned long -be32_to_cpu(unsigned long v) -{ - return cpu_to_be32 (v); -} - -#endif - -static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend; - -static int -cairo_pdf_font_use_glyph (cairo_pdf_font_t *font, int glyph) -{ - return font->backend->use_glyph (font, glyph); -} - -static cairo_status_t -cairo_pdf_font_generate (cairo_pdf_font_t *font, - const char **data, unsigned long *length) -{ - return font->backend->generate (font, data, length); -} - -static void -cairo_pdf_font_destroy (cairo_pdf_font_t *font) -{ - font->backend->destroy (font); -} - -static cairo_pdf_font_t * -cairo_pdf_ft_font_create (cairo_pdf_document_t *document, - cairo_unscaled_font_t *unscaled_font) -{ - FT_Face face; - cairo_pdf_ft_font_t *font; - unsigned long size; - int i, j; - - face = _cairo_ft_unscaled_font_lock_face (unscaled_font); - - /* We currently only support freetype truetype fonts. */ - size = 0; - if (!FT_IS_SFNT (face) || - FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0) - return NULL; - - font = malloc (sizeof (cairo_pdf_ft_font_t)); - if (font == NULL) - return NULL; - - font->base.unscaled_font = unscaled_font; - _cairo_unscaled_font_reference (unscaled_font); - font->base.backend = &cairo_pdf_ft_font_backend; - font->base.font_id = _cairo_pdf_document_new_object (document); - - _cairo_array_init (&font->output, sizeof (char)); - if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) - goto fail1; - - font->base.unscaled_font = unscaled_font; - _cairo_unscaled_font_reference (unscaled_font); - font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); - if (font->glyphs == NULL) - goto fail2; - - font->parent_to_subset = calloc (face->num_glyphs, sizeof (int)); - if (font->parent_to_subset == NULL) - goto fail3; - - font->base.num_glyphs = 1; - font->base.x_min = face->bbox.xMin; - font->base.y_min = face->bbox.yMin; - font->base.x_max = face->bbox.xMax; - font->base.y_max = face->bbox.yMax; - font->base.ascent = face->ascender; - font->base.descent = face->descender; - font->base.base_font = strdup (face->family_name); - if (font->base.base_font == NULL) - goto fail4; - - for (i = 0, j = 0; font->base.base_font[j]; j++) { - if (font->base.base_font[j] == ' ') - continue; - font->base.base_font[i++] = font->base.base_font[j]; - } - font->base.base_font[i] = '\0'; - - font->base.widths = calloc (face->num_glyphs, sizeof (int)); - if (font->base.widths == NULL) - goto fail5; - - _cairo_ft_unscaled_font_unlock_face (unscaled_font); - - font->status = CAIRO_STATUS_SUCCESS; - - return &font->base; - - fail5: - free (font->base.base_font); - fail4: - free (font->parent_to_subset); - fail3: - free (font->glyphs); - fail2: - _cairo_array_fini (&font->output); - fail1: - free (font); - return NULL; -} - -static void -cairo_pdf_ft_font_destroy (void *abstract_font) -{ - cairo_pdf_ft_font_t *font = abstract_font; - - _cairo_unscaled_font_destroy (font->base.unscaled_font); - free (font->base.base_font); - free (font->parent_to_subset); - free (font->glyphs); - _cairo_array_fini (&font->output); - free (font); -} - -static void * -cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font, - const void *data, size_t length) -{ - void *p; - - p = _cairo_array_append (&font->output, data, length); - if (p == NULL) - font->status = CAIRO_STATUS_NO_MEMORY; - - return p; -} - -static void -cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font, - unsigned short value) -{ - unsigned short be16_value; - - be16_value = cpu_to_be16 (value); - cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value); -} - -static void -cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value) -{ - unsigned long be32_value; - - be32_value = cpu_to_be32 (value); - cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value); -} - -static unsigned long -cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font) -{ - int length, aligned; - static const char pad[4]; - - length = _cairo_array_num_elements (&font->output); - aligned = (length + 3) & ~3; - cairo_pdf_ft_font_write (font, pad, aligned - length); - - return aligned; -} - -static int -cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag) -{ - int i; - - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 1); - - cairo_pdf_ft_font_write_be16 (font, 1); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be32 (font, 12); - - /* Output a format 6 encoding table. */ - - cairo_pdf_ft_font_write_be16 (font, 6); - cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1)); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */ - cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1); - for (i = 1; i < font->base.num_glyphs; i++) - cairo_pdf_ft_font_write_be16 (font, i); - - return font->status; -} - -static int -cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - char *buffer; - unsigned long size; - - size = 0; - FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size); - buffer = cairo_pdf_ft_font_write (font, NULL, size); - FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size); - - return 0; -} - -static int -cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - unsigned long start_offset, index, size; - TT_Header *header; - unsigned long begin, end; - char *buffer; - int i; - union { - unsigned char *bytes; - unsigned short *short_offsets; - unsigned long *long_offsets; - } u; - - header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); - if (header->Index_To_Loc_Format == 0) - size = sizeof (short) * (font->face->num_glyphs + 1); - else - size = sizeof (long) * (font->face->num_glyphs + 1); - - u.bytes = malloc (size); - if (u.bytes == NULL) { - font->status = CAIRO_STATUS_NO_MEMORY; - return font->status; - } - FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size); - - start_offset = _cairo_array_num_elements (&font->output); - for (i = 0; i < font->base.num_glyphs; i++) { - index = font->glyphs[i].parent_index; - if (header->Index_To_Loc_Format == 0) { - begin = be16_to_cpu (u.short_offsets[index]) * 2; - end = be16_to_cpu (u.short_offsets[index + 1]) * 2; - } - else { - begin = be32_to_cpu (u.long_offsets[index]); - end = be32_to_cpu (u.long_offsets[index + 1]); - } - - size = end - begin; - - font->glyphs[i].location = - cairo_pdf_ft_font_align_output (font) - start_offset; - buffer = cairo_pdf_ft_font_write (font, NULL, size); - if (buffer == NULL) - break; - FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size); - /* FIXME: remap composite glyphs */ - } - - font->glyphs[i].location = - cairo_pdf_ft_font_align_output (font) - start_offset; - - free (u.bytes); - - return font->status; -} - -static int -cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - TT_Header *head; - - head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); - - cairo_pdf_ft_font_write_be32 (font, head->Table_Version); - cairo_pdf_ft_font_write_be32 (font, head->Font_Revision); - - font->checksum_location = - (unsigned long *) _cairo_array_index (&font->output, 0) + - _cairo_array_num_elements (&font->output) / sizeof (long); - cairo_pdf_ft_font_write_be32 (font, 0); - cairo_pdf_ft_font_write_be32 (font, head->Magic_Number); - - cairo_pdf_ft_font_write_be16 (font, head->Flags); - cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM); - - cairo_pdf_ft_font_write_be32 (font, head->Created[0]); - cairo_pdf_ft_font_write_be32 (font, head->Created[1]); - cairo_pdf_ft_font_write_be32 (font, head->Modified[0]); - cairo_pdf_ft_font_write_be32 (font, head->Modified[1]); - - cairo_pdf_ft_font_write_be16 (font, head->xMin); - cairo_pdf_ft_font_write_be16 (font, head->yMin); - cairo_pdf_ft_font_write_be16 (font, head->xMax); - cairo_pdf_ft_font_write_be16 (font, head->yMax); - - cairo_pdf_ft_font_write_be16 (font, head->Mac_Style); - cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM); - - cairo_pdf_ft_font_write_be16 (font, head->Font_Direction); - cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format); - cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format); - - return font->status; -} - -static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag) -{ - TT_HoriHeader *hhea; - - hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea); - - cairo_pdf_ft_font_write_be32 (font, hhea->Version); - cairo_pdf_ft_font_write_be16 (font, hhea->Ascender); - cairo_pdf_ft_font_write_be16 (font, hhea->Descender); - cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap); - - cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max); - - cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing); - cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing); - cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent); - cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise); - cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run); - cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset); - - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 0); - cairo_pdf_ft_font_write_be16 (font, 0); - - cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format); - cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); - - return font->status; -} - -static int -cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - unsigned long entry_size; - short *p; - int i; - - for (i = 0; i < font->base.num_glyphs; i++) { - entry_size = 2 * sizeof (short); - p = cairo_pdf_ft_font_write (font, NULL, entry_size); - FT_Load_Sfnt_Table (font->face, TTAG_hmtx, - font->glyphs[i].parent_index * entry_size, - (FT_Byte *) p, &entry_size); - font->base.widths[i] = be16_to_cpu (p[0]); - } - - return font->status; -} - -static int -cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - int i; - TT_Header *header; - - header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); - - if (header->Index_To_Loc_Format == 0) { - for (i = 0; i < font->base.num_glyphs + 1; i++) - cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2); - } - else { - for (i = 0; i < font->base.num_glyphs + 1; i++) - cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location); - } - - return font->status; -} - -static int -cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font, - unsigned long tag) -{ - TT_MaxProfile *maxp; - - maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp); - - cairo_pdf_ft_font_write_be32 (font, maxp->version); - cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); - cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints); - cairo_pdf_ft_font_write_be16 (font, maxp->maxContours); - cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints); - cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours); - cairo_pdf_ft_font_write_be16 (font, maxp->maxZones); - cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints); - cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage); - cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs); - cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs); - cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements); - cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions); - cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements); - cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth); - - return font->status; -} - -typedef struct table table_t; -struct table { - unsigned long tag; - int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag); -}; - -static const table_t truetype_tables[] = { - { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table }, - { TTAG_cvt, cairo_pdf_ft_font_write_generic_table }, - { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table }, - { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table }, - { TTAG_head, cairo_pdf_ft_font_write_head_table }, - { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table }, - { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table }, - { TTAG_loca, cairo_pdf_ft_font_write_loca_table }, - { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table }, - { TTAG_name, cairo_pdf_ft_font_write_generic_table }, - { TTAG_prep, cairo_pdf_ft_font_write_generic_table }, -}; - -static cairo_status_t -cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font) -{ - unsigned short search_range, entry_selector, range_shift; - int num_tables; - - num_tables = ARRAY_LENGTH (truetype_tables); - search_range = 1; - entry_selector = 0; - while (search_range * 2 <= num_tables) { - search_range *= 2; - entry_selector++; - } - search_range *= 16; - range_shift = num_tables * 16 - search_range; - - cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION); - cairo_pdf_ft_font_write_be16 (font, num_tables); - cairo_pdf_ft_font_write_be16 (font, search_range); - cairo_pdf_ft_font_write_be16 (font, entry_selector); - cairo_pdf_ft_font_write_be16 (font, range_shift); - - cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16); - - return font->status; -} - -static unsigned long -cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font, - unsigned long start, unsigned long end) -{ - unsigned long *padded_end; - unsigned long *p; - unsigned long checksum; - char *data; - - checksum = 0; - data = _cairo_array_index (&font->output, 0); - p = (unsigned long *) (data + start); - padded_end = (unsigned long *) (data + ((end + 3) & ~3)); - while (p < padded_end) - checksum += *p++; - - return checksum; -} - -static void -cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag, - unsigned long start, unsigned long end) -{ - unsigned long *entry; - - entry = _cairo_array_index (&font->output, 12 + 16 * index); - entry[0] = cpu_to_be32 (tag); - entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end)); - entry[2] = cpu_to_be32 (start); - entry[3] = cpu_to_be32 (end - start); -} - -static cairo_status_t -cairo_pdf_ft_font_generate (void *abstract_font, - const char **data, unsigned long *length) -{ - cairo_pdf_ft_font_t *font = abstract_font; - unsigned long start, end, next, checksum; - int i; - - font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font); - - if (cairo_pdf_ft_font_write_offset_table (font)) - goto fail; - - start = cairo_pdf_ft_font_align_output (font); - end = start; - - end = 0; - for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { - if (truetype_tables[i].write (font, truetype_tables[i].tag)) - goto fail; - - end = _cairo_array_num_elements (&font->output); - next = cairo_pdf_ft_font_align_output (font); - cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag, - start, end); - start = next; - } - - checksum = - 0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end); - *font->checksum_location = cpu_to_be32 (checksum); - - *data = _cairo_array_index (&font->output, 0); - *length = _cairo_array_num_elements (&font->output); - - fail: - _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font); - font->face = NULL; - - return font->status; -} - -static int -cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph) -{ - cairo_pdf_ft_font_t *font = abstract_font; - - if (font->parent_to_subset[glyph] == 0) { - font->parent_to_subset[glyph] = font->base.num_glyphs; - font->glyphs[font->base.num_glyphs].parent_index = glyph; - font->base.num_glyphs++; - } - - return font->parent_to_subset[glyph]; -} - -static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend = { - cairo_pdf_ft_font_use_glyph, - cairo_pdf_ft_font_generate, - cairo_pdf_ft_font_destroy -}; - -/* PDF Generation */ - -static unsigned int -_cairo_pdf_document_new_object (cairo_pdf_document_t *document) -{ - cairo_pdf_object_t object; - - object.offset = ftell (document->file); - if (_cairo_array_append (&document->objects, &object, 1) == NULL) - return 0; - - return document->next_available_id++; -} - -static void -_cairo_pdf_document_update_object (cairo_pdf_document_t *document, - unsigned int id) -{ - cairo_pdf_object_t *object; - - object = _cairo_array_index (&document->objects, id - 1); - object->offset = ftell (document->file); -} - -static void -_cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface, - cairo_pdf_stream_t *stream) -{ - _cairo_array_append (&surface->streams, &stream, 1); - surface->current_stream = stream; -} - -static void -_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, unsigned int id) -{ - cairo_pdf_resource_t resource; - - resource.id = id; - _cairo_array_append (&surface->patterns, &resource, 1); -} - -static void -_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, unsigned int id) -{ - cairo_pdf_resource_t resource; - int i, num_resources; - - num_resources = _cairo_array_num_elements (&surface->xobjects); - for (i = 0; i < num_resources; i++) { - _cairo_array_copy_element (&surface->xobjects, i, &resource); - if (resource.id == id) - return; - } - - resource.id = id; - _cairo_array_append (&surface->xobjects, &resource, 1); -} - -static unsigned int -_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha) -{ - int num_alphas, i; - double other; - - num_alphas = _cairo_array_num_elements (&surface->alphas); - for (i = 0; i < num_alphas; i++) { - _cairo_array_copy_element (&surface->alphas, i, &other); - if (alpha == other) - return i; - } - - _cairo_array_append (&surface->alphas, &alpha, 1); - return _cairo_array_num_elements (&surface->alphas) - 1; -} - -static void -_cairo_pdf_surface_add_font (cairo_pdf_surface_t *surface, unsigned int id) -{ - cairo_pdf_resource_t resource; - int i, num_fonts; - - num_fonts = _cairo_array_num_elements (&surface->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&surface->fonts, i, &resource); - if (resource.id == id) - return; - } - - resource.id = id; - _cairo_array_append (&surface->fonts, &resource, 1); -} - -cairo_surface_t * -cairo_pdf_surface_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_pdf_document_t *document; - cairo_surface_t *surface; - - document = _cairo_pdf_document_create (file, - width_inches, - height_inches, - x_pixels_per_inch, - y_pixels_per_inch); - if (document == NULL) - return NULL; - - surface = _cairo_pdf_surface_create_for_document (document, - width_inches, - height_inches); - - _cairo_pdf_document_destroy (document); - - return surface; -} - -static cairo_surface_t * -_cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, - double width_inches, - double height_inches) -{ - cairo_pdf_surface_t *surface; - - surface = malloc (sizeof (cairo_pdf_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init (&surface->base, &cairo_pdf_surface_backend); - - surface->width_inches = width_inches; - surface->height_inches = height_inches; - - _cairo_pdf_document_reference (document); - surface->document = document; - _cairo_array_init (&surface->streams, sizeof (cairo_pdf_stream_t *)); - _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t)); - _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t)); - _cairo_array_init (&surface->alphas, sizeof (double)); - _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_resource_t)); - - return &surface->base; -} - -static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) -{ - int num_streams, i; - cairo_pdf_stream_t *stream; - - num_streams = _cairo_array_num_elements (&surface->streams); - for (i = 0; i < num_streams; i++) { - _cairo_array_copy_element (&surface->streams, i, &stream); - free (stream); - } - - _cairo_array_truncate (&surface->streams, 0); - _cairo_array_truncate (&surface->patterns, 0); - _cairo_array_truncate (&surface->xobjects, 0); - _cairo_array_truncate (&surface->alphas, 0); - _cairo_array_truncate (&surface->fonts, 0); -} - -static cairo_surface_t * -_cairo_pdf_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - cairo_pdf_surface_t *template = abstract_src; - - return _cairo_pdf_surface_create_for_document (template->document, - width, height); -} - -static cairo_pdf_stream_t * -_cairo_pdf_document_open_stream (cairo_pdf_document_t *document, - const char *extra_entries) -{ - FILE *file = document->file; - cairo_pdf_stream_t *stream; - - stream = malloc (sizeof (cairo_pdf_stream_t)); - if (stream == NULL) { - return NULL; - } - - stream->id = _cairo_pdf_document_new_object (document); - stream->length_id = _cairo_pdf_document_new_object (document); - - fprintf (file, - "%d 0 obj\r\n" - "<< /Length %d 0 R\r\n" - "%s" - ">>\r\n" - "stream\r\n", - stream->id, - stream->length_id, - extra_entries); - - stream->start_offset = ftell (file); - - document->current_stream = stream; - - return stream; -} - -static void -_cairo_pdf_document_close_stream (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - long length; - cairo_pdf_stream_t *stream; - - stream = document->current_stream; - if (stream == NULL) - return; - - length = ftell(file) - stream->start_offset; - fprintf (file, - "\r\n" - "endstream\r\n" - "endobj\r\n"); - - _cairo_pdf_document_update_object (document, stream->length_id); - fprintf (file, - "%d 0 obj\r\n" - " %ld\r\n" - "endobj\r\n", - stream->length_id, - length); - - document->current_stream = NULL; -} - -static void -_cairo_pdf_surface_destroy (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_document_t *document = surface->document; - - if (surface->current_stream == document->current_stream) - _cairo_pdf_document_close_stream (document); - - _cairo_pdf_document_destroy (document); - - free (surface); -} - -/* XXX: We should re-work this interface to return both X/Y ppi values. */ -static double -_cairo_pdf_surface_pixels_per_inch (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - return surface->document->y_ppi; -} - -static void -_cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface) -{ - cairo_pdf_document_t *document = surface->document; - cairo_pdf_stream_t *stream; - FILE *file = document->file; - char extra[200]; - - if (document->current_stream == NULL || - document->current_stream != surface->current_stream) { - _cairo_pdf_document_close_stream (document); - snprintf (extra, sizeof extra, - " /Type /XObject\r\n" - " /Subtype /Form\r\n" - " /BBox [ 0 0 %f %f ]\r\n", - surface->width_inches * document->x_ppi, - surface->height_inches * document->y_ppi); - stream = _cairo_pdf_document_open_stream (document, extra); - _cairo_pdf_surface_add_stream (surface, stream); - - /* If this is the first stream we open for this surface, - * output the cairo to PDF transformation matrix. */ - if (_cairo_array_num_elements (&surface->streams) == 1) - fprintf (file, "1 0 0 -1 0 %f cm\r\n", - document->height_inches * document->y_ppi); - } -} - -static cairo_status_t -_cairo_pdf_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_pdf_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_pdf_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_pdf_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_pdf_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void * -compress_dup (const void *data, unsigned long data_size, - unsigned long *compressed_size) -{ - void *compressed; - - /* Bound calculation taken from zlib. */ - *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11; - compressed = malloc (*compressed_size); - if (compressed == NULL) - return NULL; - - compress (compressed, compressed_size, data, data_size); - - return compressed; -} - -static unsigned int -emit_image_data (cairo_pdf_document_t *document, - cairo_image_surface_t *image) -{ - FILE *file = document->file; - cairo_pdf_stream_t *stream; - char entries[200]; - char *rgb, *compressed; - int i, x, y; - unsigned long rgb_size, compressed_size; - pixman_bits_t *pixel; - - rgb_size = image->height * image->width * 3; - rgb = malloc (rgb_size); - if (rgb == NULL) - return 0; - - i = 0; - for (y = 0; y < image->height; y++) { - pixel = (pixman_bits_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel++) { - rgb[i++] = (*pixel & 0x00ff0000) >> 16; - rgb[i++] = (*pixel & 0x0000ff00) >> 8; - rgb[i++] = (*pixel & 0x000000ff) >> 0; - } - } - - compressed = compress_dup (rgb, rgb_size, &compressed_size); - if (compressed == NULL) { - free (rgb); - return 0; - } - - _cairo_pdf_document_close_stream (document); - - snprintf (entries, sizeof entries, - " /Type /XObject\r\n" - " /Subtype /Image\r\n" - " /Width %d\r\n" - " /Height %d\r\n" - " /ColorSpace /DeviceRGB\r\n" - " /BitsPerComponent 8\r\n" - " /Filter /FlateDecode\r\n", - image->width, image->height); - - stream = _cairo_pdf_document_open_stream (document, entries); - fwrite (compressed, 1, compressed_size, file); - _cairo_pdf_document_close_stream (document); - - free (rgb); - free (compressed); - - return stream->id; -} - -static cairo_int_status_t -_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, - cairo_surface_pattern_t *pattern) -{ - cairo_pdf_document_t *document = dst->document; - FILE *file = document->file; - unsigned id; - cairo_matrix_t i2u; - cairo_status_t status; - cairo_image_surface_t *image; - cairo_surface_t *src; - void *image_extra; - - src = pattern->surface; - status = _cairo_surface_acquire_source_image (src, &image, &image_extra); - if (!CAIRO_OK (status)) - return status; - - id = emit_image_data (dst->document, image); - if (id == 0) { - status = CAIRO_STATUS_NO_MEMORY; - goto bail; - } - - _cairo_pdf_surface_add_xobject (dst, id); - - _cairo_pdf_surface_ensure_stream (dst); - - cairo_matrix_copy (&i2u, &pattern->base.matrix); - cairo_matrix_invert (&i2u); - cairo_matrix_translate (&i2u, 0, image->height); - cairo_matrix_scale (&i2u, image->width, -image->height); - - fprintf (file, - "q %f %f %f %f %f %f cm /res%d Do Q\r\n", - i2u.m[0][0], i2u.m[0][1], - i2u.m[1][0], i2u.m[1][1], - i2u.m[2][0], i2u.m[2][1], - id); - - bail: - _cairo_surface_release_source_image (src, image, image_extra); - - return status; -} - -/* The contents of the surface is already transformed into PDF units, - * but when we composite the surface we may want to use a different - * space. The problem I see now is that the show_surface snippet - * creates a surface 1x1, which in the snippet environment is the - * entire surface. When compositing the surface, cairo gives us the - * 1x1 to 256x256 matrix. This would be fine if cairo didn't actually - * also transform the drawing to the surface. Should the CTM be part - * of the current target surface? - */ - -static cairo_int_status_t -_cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, - cairo_surface_pattern_t *pattern) -{ - cairo_pdf_document_t *document = dst->document; - FILE *file = document->file; - cairo_matrix_t i2u; - cairo_pdf_stream_t *stream; - int num_streams, i; - cairo_pdf_surface_t *src; - - _cairo_pdf_surface_ensure_stream (dst); - - src = (cairo_pdf_surface_t *) pattern->surface; - - cairo_matrix_copy (&i2u, &src->base.matrix); - cairo_matrix_invert (&i2u); - cairo_matrix_scale (&i2u, - 1.0 / (src->width_inches * document->x_ppi), - 1.0 / (src->height_inches * document->y_ppi)); - - fprintf (file, - "q %f %f %f %f %f %f cm", - i2u.m[0][0], i2u.m[0][1], - i2u.m[1][0], i2u.m[1][1], - i2u.m[2][0], i2u.m[2][1]); - - num_streams = _cairo_array_num_elements (&src->streams); - for (i = 0; i < num_streams; i++) { - _cairo_array_copy_element (&src->streams, i, &stream); - fprintf (file, - " /res%d Do", - stream->id); - - _cairo_pdf_surface_add_xobject (dst, stream->id); - - } - - fprintf (file, " Q\r\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_pdf_surface_t *dst = abstract_dst; - cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) src_pattern; - - if (mask_pattern) - return CAIRO_STATUS_SUCCESS; - - if (src_pattern->type != CAIRO_PATTERN_SURFACE) - return CAIRO_STATUS_SUCCESS; - - if (src->surface->backend == &cairo_pdf_surface_backend) - return _cairo_pdf_surface_composite_pdf (dst, src); - else - return _cairo_pdf_surface_composite_image (dst, src); -} - -static cairo_int_status_t -_cairo_pdf_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - int i; - - _cairo_pdf_surface_ensure_stream (surface); - - fprintf (file, - "%f %f %f rg\r\n", - color->red, color->green, color->blue); - - for (i = 0; i < num_rects; i++) { - fprintf (file, - "%d %d %d %d re f\r\n", - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -emit_solid_pattern (cairo_pdf_surface_t *surface, - cairo_solid_pattern_t *pattern) -{ - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - unsigned int alpha; - - alpha = _cairo_pdf_surface_add_alpha (surface, pattern->base.alpha); - _cairo_pdf_surface_ensure_stream (surface); - fprintf (file, - "%f %f %f rg /a%d gs\r\n", - pattern->red, - pattern->green, - pattern->blue, - alpha); -} - -static void -emit_surface_pattern (cairo_pdf_surface_t *dst, - cairo_surface_pattern_t *pattern) -{ - cairo_pdf_document_t *document = dst->document; - FILE *file = document->file; - cairo_pdf_stream_t *stream; - cairo_image_surface_t *image; - void *image_extra; - cairo_status_t status; - char entries[250]; - unsigned int id, alpha; - cairo_matrix_t pm; - - if (pattern->surface->backend == &cairo_pdf_surface_backend) { - return; - } - - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (!CAIRO_OK (status)) - return; - - _cairo_pdf_document_close_stream (document); - - id = emit_image_data (dst->document, image); - - /* BBox must be smaller than XStep by YStep or acroread wont - * display the pattern. */ - - cairo_matrix_set_identity (&pm); - cairo_matrix_scale (&pm, image->width, image->height); - cairo_matrix_copy (&pm, &pattern->base.matrix); - cairo_matrix_invert (&pm); - - snprintf (entries, sizeof entries, - " /BBox [ 0 0 256 256 ]\r\n" - " /XStep 256\r\n" - " /YStep 256\r\n" - " /PatternType 1\r\n" - " /TilingType 1\r\n" - " /PaintType 1\r\n" - " /Resources << /XObject << /res%d %d 0 R >> >>\r\n" - " /Matrix [ %f %f %f %f %f %f ]\r\n", - id, id, - pm.m[0][0], pm.m[0][1], - pm.m[1][0], pm.m[1][1], - pm.m[2][0], pm.m[2][1]); - - stream = _cairo_pdf_document_open_stream (document, entries); - - /* FIXME: emit code to show surface here. */ - - _cairo_pdf_surface_add_pattern (dst, stream->id); - - _cairo_pdf_surface_ensure_stream (dst); - alpha = _cairo_pdf_surface_add_alpha (dst, 1.0); - fprintf (file, - "/Pattern cs /res%d scn /a%d gs\r\n", - stream->id, alpha); - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); -} - -static unsigned int -emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern) -{ - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - unsigned int function_id; - - function_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /FunctionType 0\r\n" - " /Domain [ 0.0 1.0 ]\r\n" - " /Size [ 2 ]\r\n" - " /BitsPerSample 8\r\n" - " /Range [ 0.0 1.0 0.0 1.0 0.0 1.0 ]\r\n" - " /Length 6\r\n" - ">>\r\n" - "stream\r\n", - function_id); - - fputc (pattern->stops[0].color.red * 0xff, file); - fputc (pattern->stops[0].color.green * 0xff, file); - fputc (pattern->stops[0].color.blue * 0xff, file); - fputc (pattern->stops[1].color.red * 0xff, file); - fputc (pattern->stops[1].color.green * 0xff, file); - fputc (pattern->stops[1].color.blue * 0xff, file); - - fprintf (file, - "\r\n" - "endstream\r\n" - "endobj\r\n"); - - return function_id; -} - -static void -emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *pattern) -{ - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - unsigned int function_id, pattern_id, alpha; - double x0, y0, x1, y1; - cairo_matrix_t p2u; - - _cairo_pdf_document_close_stream (document); - - function_id = emit_pattern_stops (surface, &pattern->base); - - cairo_matrix_copy (&p2u, &pattern->base.base.matrix); - cairo_matrix_invert (&p2u); - - x0 = pattern->point0.x; - y0 = pattern->point0.y; - cairo_matrix_transform_point (&p2u, &x0, &y0); - x1 = pattern->point1.x; - y1 = pattern->point1.y; - cairo_matrix_transform_point (&p2u, &x1, &y1); - - pattern_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Pattern\r\n" - " /PatternType 2\r\n" - " /Matrix [ 1 0 0 -1 0 %f ]\r\n" - " /Shading\r\n" - " << /ShadingType 2\r\n" - " /ColorSpace /DeviceRGB\r\n" - " /Coords [ %f %f %f %f ]\r\n" - " /Function %d 0 R\r\n" - " /Extend [ true true ]\r\n" - " >>\r\n" - ">>\r\n" - "endobj\r\n", - pattern_id, - document->height_inches * document->y_ppi, - x0, y0, x1, y1, - function_id); - - _cairo_pdf_surface_add_pattern (surface, pattern_id); - - _cairo_pdf_surface_ensure_stream (surface); - alpha = _cairo_pdf_surface_add_alpha (surface, 1.0); - - /* Use pattern */ - fprintf (file, - "/Pattern cs /res%d scn /a%d gs\r\n", - pattern_id, alpha); -} - -static void -emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *pattern) -{ - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - unsigned int function_id, pattern_id, alpha; - double x0, y0, x1, y1, r0, r1; - cairo_matrix_t p2u; - - _cairo_pdf_document_close_stream (document); - - function_id = emit_pattern_stops (surface, &pattern->base); - - cairo_matrix_copy (&p2u, &pattern->base.base.matrix); - cairo_matrix_invert (&p2u); - - x0 = pattern->center0.x; - y0 = pattern->center0.y; - r0 = pattern->radius0; - cairo_matrix_transform_point (&p2u, &x0, &y0); - x1 = pattern->center1.x; - y1 = pattern->center1.y; - r1 = pattern->radius1; - cairo_matrix_transform_point (&p2u, &x1, &y1); - - /* FIXME: This is surely crack, but how should you scale a radius - * in a non-orthogonal coordinate system? */ - cairo_matrix_transform_distance (&p2u, &r0, &r1); - - /* FIXME: There is a difference between the cairo gradient extend - * semantics and PDF extend semantics. PDFs extend=false means - * that nothing is painted outside the gradient boundaries, - * whereas cairo takes this to mean that the end color is padded - * to infinity. Setting extend=true in PDF gives the cairo default - * behavoir, not yet sure how to implement the cairo mirror and - * repeat behaviour. */ - pattern_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Pattern\r\n" - " /PatternType 2\r\n" - " /Matrix [ 1 0 0 -1 0 %f ]\r\n" - " /Shading\r\n" - " << /ShadingType 3\r\n" - " /ColorSpace /DeviceRGB\r\n" - " /Coords [ %f %f %f %f %f %f ]\r\n" - " /Function %d 0 R\r\n" - " /Extend [ true true ]\r\n" - " >>\r\n" - ">>\r\n" - "endobj\r\n", - pattern_id, - document->height_inches * document->y_ppi, - x0, y0, r0, x1, y1, r1, - function_id); - - _cairo_pdf_surface_add_pattern (surface, pattern_id); - - _cairo_pdf_surface_ensure_stream (surface); - alpha = _cairo_pdf_surface_add_alpha (surface, 1.0); - - /* Use pattern */ - fprintf (file, - "/Pattern cs /res%d scn /a%d gs\r\n", - pattern_id, alpha); -} - -static void -emit_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) -{ - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: - emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); - break; - - case CAIRO_PATTERN_SURFACE: - emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); - break; - - case CAIRO_PATTERN_LINEAR: - emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); - break; - - case CAIRO_PATTERN_RADIAL: - emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); - break; - } -} - -static double -intersect (cairo_line_t *line, cairo_fixed_t y) -{ - return _cairo_fixed_to_double (line->p1.x) + - _cairo_fixed_to_double (line->p2.x - line->p1.x) * - _cairo_fixed_to_double (y - line->p1.y) / - _cairo_fixed_to_double (line->p2.y - line->p1.y); -} - -static cairo_int_status_t -_cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - 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_pdf_surface_t *surface = abstract_dst; - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - int i; - - emit_pattern (surface, pattern); - - /* After the above switch the current stream should belong to this - * surface, so no need to _cairo_pdf_surface_ensure_stream() */ - assert (document->current_stream != NULL && - document->current_stream == surface->current_stream); - - for (i = 0; i < num_traps; i++) { - double left_x1, left_x2, right_x1, right_x2; - - left_x1 = intersect (&traps[i].left, traps[i].top); - left_x2 = intersect (&traps[i].left, traps[i].bottom); - right_x1 = intersect (&traps[i].right, traps[i].top); - right_x2 = intersect (&traps[i].right, traps[i].bottom); - - fprintf (file, - "%f %f m %f %f l %f %f l %f %f l h\r\n", - left_x1, _cairo_fixed_to_double (traps[i].top), - left_x2, _cairo_fixed_to_double (traps[i].bottom), - right_x2, _cairo_fixed_to_double (traps[i].bottom), - right_x1, _cairo_fixed_to_double (traps[i].top)); - } - - fprintf (file, - "f\r\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_copy_page (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_document_t *document = surface->document; - - return _cairo_pdf_document_add_page (document, surface); -} - -static cairo_int_status_t -_cairo_pdf_surface_show_page (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_document_t *document = surface->document; - cairo_int_status_t status; - - status = _cairo_pdf_document_add_page (document, surface); - if (status == CAIRO_STATUS_SUCCESS) - _cairo_pdf_surface_clear (surface); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_pdf_font_t * -_cairo_pdf_document_get_font (cairo_pdf_document_t *document, - cairo_font_t *font) -{ - cairo_unscaled_font_t *unscaled_font; - cairo_pdf_font_t *pdf_font; - unsigned int num_fonts, i; - - unscaled_font = _cairo_ft_font_get_unscaled_font (font); - - num_fonts = _cairo_array_num_elements (&document->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&document->fonts, i, &pdf_font); - if (pdf_font->unscaled_font == unscaled_font) - return pdf_font; - } - - /* FIXME: Figure out here which font backend is in use and call - * the appropriate constructor. */ - pdf_font = cairo_pdf_ft_font_create (document, unscaled_font); - if (pdf_font == NULL) - return NULL; - - if (_cairo_array_append (&document->fonts, &pdf_font, 1) == NULL) { - cairo_pdf_font_destroy (pdf_font); - return NULL; - } - - return pdf_font; -} - -static cairo_status_t -_cairo_pdf_surface_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_document_t *document = surface->document; - FILE *file = document->file; - cairo_pdf_font_t *pdf_font; - int i, index; - - pdf_font = _cairo_pdf_document_get_font (document, font); - if (pdf_font == NULL) - return CAIRO_STATUS_NO_MEMORY; - - emit_pattern (surface, pattern); - - fprintf (file, "BT /res%u 1 Tf", pdf_font->font_id); - for (i = 0; i < num_glyphs; i++) { - - index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); - - fprintf (file, - " %f %f %f %f %f %f Tm (\\%o) Tj", - font->scale.matrix[0][0], - font->scale.matrix[0][1], - font->scale.matrix[1][0], - -font->scale.matrix[1][1], - glyphs[i].x, - glyphs[i].y, - index); - } - fprintf (file, " ET\r\n"); - - _cairo_pdf_surface_add_font (surface, pdf_font->font_id); - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_pdf_surface_backend = { - _cairo_pdf_surface_create_similar, - _cairo_pdf_surface_destroy, - _cairo_pdf_surface_pixels_per_inch, - _cairo_pdf_surface_acquire_source_image, - _cairo_pdf_surface_release_source_image, - _cairo_pdf_surface_acquire_dest_image, - _cairo_pdf_surface_release_dest_image, - _cairo_pdf_surface_clone_similar, - _cairo_pdf_surface_composite, - _cairo_pdf_surface_fill_rectangles, - _cairo_pdf_surface_composite_trapezoids, - _cairo_pdf_surface_copy_page, - _cairo_pdf_surface_show_page, - _cairo_pdf_surface_set_clip_region, - _cairo_pdf_surface_show_glyphs -}; - -static cairo_pdf_document_t * -_cairo_pdf_document_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_pdf_document_t *document; - - document = malloc (sizeof (cairo_pdf_document_t)); - if (document == NULL) - return NULL; - - document->file = file; - document->refcount = 1; - document->width_inches = width_inches; - document->height_inches = height_inches; - document->x_ppi = x_pixels_per_inch; - document->y_ppi = y_pixels_per_inch; - - _cairo_array_init (&document->objects, sizeof (cairo_pdf_object_t)); - _cairo_array_init (&document->pages, sizeof (unsigned int)); - document->next_available_id = 1; - - document->current_stream = NULL; - - document->pages_id = _cairo_pdf_document_new_object (document); - - _cairo_array_init (&document->fonts, sizeof (cairo_pdf_font_t *)); - - /* Document header */ - fprintf (file, "%%PDF-1.4\r\n"); - - return document; -} - -static unsigned int -_cairo_pdf_document_write_info (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - unsigned int id; - - id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Creator (cairographics.org)\r\n" - " /Producer (cairographics.org)\r\n" - ">>\r\n" - "endobj\r\n", - id); - - return id; -} - -static void -_cairo_pdf_document_write_pages (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - unsigned int page_id; - int num_pages, i; - - _cairo_pdf_document_update_object (document, document->pages_id); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Pages\r\n" - " /Kids [ ", - document->pages_id); - - num_pages = _cairo_array_num_elements (&document->pages); - for (i = 0; i < num_pages; i++) { - _cairo_array_copy_element (&document->pages, i, &page_id); - fprintf (file, "%d 0 R ", page_id); - } - - fprintf (file, "]\r\n"); - fprintf (file, " /Count %d\r\n", num_pages); - - /* TODO: Figure out wich other defaults to be inherited by /Page - * objects. */ - fprintf (file, - " /MediaBox [ 0 0 %f %f ]\r\n" - ">>\r\n" - "endobj\r\n", - document->width_inches * document->x_ppi, - document->height_inches * document->y_ppi); -} - -static cairo_status_t -_cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - cairo_pdf_font_t *font; - int num_fonts, i, j; - const char *data; - char *compressed; - unsigned long data_size, compressed_size; - unsigned int stream_id, descriptor_id; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - num_fonts = _cairo_array_num_elements (&document->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&document->fonts, i, &font); - - status = cairo_pdf_font_generate (font, &data, &data_size); - if (status) - goto fail; - - compressed = compress_dup (data, data_size, &compressed_size); - if (compressed == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto fail; - } - - stream_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Filter /FlateDecode\r\n" - " /Length %lu\r\n" - " /Length1 %lu\r\n" - ">>\r\n" - "stream\r\n", - stream_id, - compressed_size, - data_size); - fwrite (compressed, 1, compressed_size, file); - fprintf (file, - "\r\n" - "endstream\r\n" - "endobj\r\n"); - free (compressed); - - descriptor_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /FontDescriptor\r\n" - " /FontName /7%s\r\n" - " /Flags 4\r\n" - " /FontBBox [ %ld %ld %ld %ld ]\r\n" - " /ItalicAngle 0\r\n" - " /Ascent %ld\r\n" - " /Descent %ld\r\n" - " /CapHeight 500\r\n" - " /StemV 80\r\n" - " /StemH 80\r\n" - " /FontFile2 %u 0 R\r\n" - ">>\r\n" - "endobj\r\n", - descriptor_id, - font->base_font, - font->x_min, - font->y_min, - font->x_max, - font->y_max, - font->ascent, - font->descent, - stream_id); - - _cairo_pdf_document_update_object (document, font->font_id); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Font\r\n" - " /Subtype /TrueType\r\n" - " /BaseFont /%s\r\n" - " /FirstChar 0\r\n" - " /LastChar %d\r\n" - " /FontDescriptor %d 0 R\r\n" - " /Widths ", - font->font_id, - font->base_font, - font->num_glyphs, - descriptor_id); - - fprintf (file, - "["); - - for (j = 0; j < font->num_glyphs; j++) - fprintf (file, - " %d", - font->widths[j]); - - fprintf (file, - " ]\r\n" - ">>\r\n" - "endobj\r\n"); - - fail: - cairo_pdf_ft_font_destroy (font); - } - - return status; -} - -static unsigned int -_cairo_pdf_document_write_catalog (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - unsigned int id; - - id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Catalog\r\n" - " /Pages %d 0 R\r\n" - ">>\r\n" - "endobj\r\n", - id, document->pages_id); - - return id; -} - -static long -_cairo_pdf_document_write_xref (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - cairo_pdf_object_t *object; - int num_objects, i; - long offset; - - num_objects = _cairo_array_num_elements (&document->objects); - - offset = ftell(file); - fprintf (document->file, - "xref\r\n" - "%d %d\r\n", - 0, num_objects + 1); - - fprintf (file, "0000000000 65535 f\r\n"); - for (i = 0; i < num_objects; i++) { - object = _cairo_array_index (&document->objects, i); - fprintf (file, "%010ld 00000 n\r\n", object->offset); - } - - return offset; -} - -static void -_cairo_pdf_document_reference (cairo_pdf_document_t *document) -{ - document->refcount++; -} - -static void -_cairo_pdf_document_destroy (cairo_pdf_document_t *document) -{ - FILE *file = document->file; - long offset; - unsigned int info_id, catalog_id; - - document->refcount--; - if (document->refcount > 0) - return; - - _cairo_pdf_document_close_stream (document); - _cairo_pdf_document_write_pages (document); - _cairo_pdf_document_write_fonts (document); - info_id = _cairo_pdf_document_write_info (document); - catalog_id = _cairo_pdf_document_write_catalog (document); - offset = _cairo_pdf_document_write_xref (document); - - fprintf (file, - "trailer\r\n" - "<< /Size %d\r\n" - " /Root %d 0 R\r\n" - " /Info %d 0 R\r\n" - ">>\r\n", - document->next_available_id, - catalog_id, - info_id); - - fprintf (file, - "startxref\r\n" - "%ld\r\n" - "%%%%EOF\r\n", - offset); - - free (document); -} - -static cairo_status_t -_cairo_pdf_document_add_page (cairo_pdf_document_t *document, - cairo_pdf_surface_t *surface) -{ - cairo_pdf_stream_t *stream; - cairo_pdf_resource_t *res; - FILE *file = document->file; - unsigned int page_id; - double alpha; - int num_streams, num_alphas, num_resources, i; - - _cairo_pdf_document_close_stream (document); - - page_id = _cairo_pdf_document_new_object (document); - fprintf (file, - "%d 0 obj\r\n" - "<< /Type /Page\r\n" - " /Parent %d 0 R\r\n" - " /Contents [", - page_id, - document->pages_id); - - num_streams = _cairo_array_num_elements (&surface->streams); - for (i = 0; i < num_streams; i++) { - _cairo_array_copy_element (&surface->streams, i, &stream); - fprintf (file, - " %d 0 R", - stream->id); - } - - fprintf (file, - " ]\r\n" - " /Resources <<\r\n"); - - num_resources = _cairo_array_num_elements (&surface->fonts); - if (num_resources > 0) { - fprintf (file, - " /Font <<"); - - for (i = 0; i < num_resources; i++) { - res = _cairo_array_index (&surface->fonts, i); - fprintf (file, - " /res%d %d 0 R", - res->id, res->id); - } - - fprintf (file, - " >>\r\n"); - } - - num_alphas = _cairo_array_num_elements (&surface->alphas); - if (num_alphas > 0) { - fprintf (file, - " /ExtGState <<\r\n"); - - for (i = 0; i < num_alphas; i++) { - _cairo_array_copy_element (&surface->alphas, i, &alpha); - fprintf (file, - " /a%d << /ca %f >>\r\n", - i, alpha); - } - - fprintf (file, - " >>\r\n"); - } - - num_resources = _cairo_array_num_elements (&surface->patterns); - if (num_resources > 0) { - fprintf (file, - " /Pattern <<"); - for (i = 0; i < num_resources; i++) { - res = _cairo_array_index (&surface->patterns, i); - fprintf (file, - " /res%d %d 0 R", - res->id, res->id); - } - - fprintf (file, - " >>\r\n"); - } - - num_resources = _cairo_array_num_elements (&surface->xobjects); - if (num_resources > 0) { - fprintf (file, - " /XObject <<"); - - for (i = 0; i < num_resources; i++) { - res = _cairo_array_index (&surface->xobjects, i); - fprintf (file, - " /res%d %d 0 R", - res->id, res->id); - } - - fprintf (file, - " >>\r\n"); - } - - fprintf (file, - " >>\r\n" - ">>\r\n" - "endobj\r\n"); - - _cairo_array_append (&document->pages, &page_id, 1); - - return CAIRO_STATUS_SUCCESS; -} - -void -cairo_set_target_pdf (cairo_t *cr, - FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_surface_t *surface; - - surface = cairo_pdf_surface_create (file, - width_inches, - height_inches, - x_pixels_per_inch, - y_pixels_per_inch); - - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} diff --git a/src/cairo_pen.c b/src/cairo_pen.c deleted file mode 100644 index 6ecaa00b3..000000000 --- a/src/cairo_pen.c +++ /dev/null @@ -1,587 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -static int -_cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix); - -static void -_cairo_pen_compute_slopes (cairo_pen_t *pen); - -static cairo_status_t -_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon); - -cairo_status_t -_cairo_pen_init_empty (cairo_pen_t *pen) -{ - pen->radius = 0; - pen->tolerance = 0; - pen->vertices = NULL; - pen->num_vertices = 0; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate) -{ - int i; - int reflect; - double det; - - if (pen->num_vertices) { - /* XXX: It would be nice to notice that the pen is already properly constructed. - However, this test would also have to account for possible changes in the transformation - matrix. - if (pen->radius == radius && pen->tolerance == tolerance) - return CAIRO_STATUS_SUCCESS; - */ - _cairo_pen_fini (pen); - } - - pen->radius = radius; - pen->tolerance = gstate->tolerance; - - _cairo_matrix_compute_determinant (&gstate->ctm, &det); - if (det >= 0) { - reflect = 0; - } else { - reflect = 1; - } - - pen->num_vertices = _cairo_pen_vertices_needed (gstate->tolerance, - radius, - &gstate->ctm); - - pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t)); - if (pen->vertices == NULL) { - return CAIRO_STATUS_NO_MEMORY; - } - - /* - * Compute pen coordinates. To generate the right ellipse, compute points around - * a circle in user space and transform them to device space. To get a consistent - * orientation in device space, flip the pen if the transformation matrix - * is reflecting - */ - for (i=0; i < pen->num_vertices; i++) { - double theta = 2 * M_PI * i / (double) pen->num_vertices; - double dx = radius * cos (reflect ? -theta : theta); - double dy = radius * sin (reflect ? -theta : theta); - cairo_pen_vertex_t *v = &pen->vertices[i]; - cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - v->point.x = _cairo_fixed_from_double (dx); - v->point.y = _cairo_fixed_from_double (dy); - } - - _cairo_pen_compute_slopes (pen); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_pen_fini (cairo_pen_t *pen) -{ - free (pen->vertices); - pen->vertices = NULL; - - _cairo_pen_init_empty (pen); -} - -cairo_status_t -_cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other) -{ - *pen = *other; - - if (pen->num_vertices) { - pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t)); - if (pen->vertices == NULL) { - return CAIRO_STATUS_NO_MEMORY; - } - memcpy (pen->vertices, other->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t)); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) -{ - cairo_pen_vertex_t *vertices; - int num_vertices; - int i; - - num_vertices = pen->num_vertices + num_points; - vertices = realloc (pen->vertices, num_vertices * sizeof (cairo_pen_vertex_t)); - if (vertices == NULL) - return CAIRO_STATUS_NO_MEMORY; - - pen->vertices = vertices; - pen->num_vertices = num_vertices; - - /* initialize new vertices */ - for (i=0; i < num_points; i++) - pen->vertices[pen->num_vertices-num_points+i].point = point[i]; - - _cairo_hull_compute (pen->vertices, &pen->num_vertices); - - _cairo_pen_compute_slopes (pen); - - return CAIRO_STATUS_SUCCESS; -} - -/* -The circular pen in user space is transformed into an ellipse in -device space. - -We construct the pen by computing points along the circumference -using equally spaced angles. - -We show below that this approximation to the ellipse has -maximum error at the major axis of the ellipse. -So, we need to compute the length of the major axis and then -use that to compute the number of sides needed in our pen. - -Thanks to Walter Brisken <wbrisken@aoc.nrao.edu> for this -derivation: - -1. First some notation: - -All capital letters represent vectors in two dimensions. A prime ' -represents a transformed coordinate. Matrices are written in underlined -form, ie _R_. Lowercase letters represent scalar real values. - -The letter t is used to represent the greek letter theta. - -2. The question has been posed: What is the maximum expansion factor -achieved by the linear transformation - -X' = _R_ X - -where _R_ is a real-valued 2x2 matrix with entries: - -_R_ = [a b] - [c d] . - -In other words, what is the maximum radius, MAX[ |X'| ], reached for any -X on the unit circle ( |X| = 1 ) ? - - -3. Some useful formulae - -(A) through (C) below are standard double-angle formulae. (D) is a lesser -known result and is derived below: - -(A) sin^2(t) = (1 - cos(2*t))/2 -(B) cos^2(t) = (1 + cos(2*t))/2 -(C) sin(t)*cos(t) = sin(2*t)/2 -(D) MAX[a*cos(t) + b*sin(t)] = sqrt(a^2 + b^2) - -Proof of (D): - -find the maximum of the function by setting the derivative to zero: - - -a*sin(t)+b*cos(t) = 0 - -From this it follows that - - tan(t) = b/a - -and hence - - sin(t) = b/sqrt(a^2 + b^2) - -and - - cos(t) = a/sqrt(a^2 + b^2) - -Thus the maximum value is - - MAX[a*cos(t) + b*sin(t)] = (a^2 + b^2)/sqrt(a^2 + b^2) - = sqrt(a^2 + b^2) - - -4. Derivation of maximum expansion - -To find MAX[ |X'| ] we search brute force method using calculus. The unit -circle on which X is constrained is to be parameterized by t: - - X(t) = (cos(t), sin(t)) - -Thus - - X'(t) = (a*cos(t) + b*sin(t), c*cos(t) + d*sin(t)) . - -Define - - r(t) = |X'(t)| - -Thus - - r^2(t) = (a*cos(t) + b*sin(t))^2 + (c*cos(t) + d*sin(t))^2 - = (a^2 + c^2)*cos^2(t) + (b^2 + d^2)*sin^2(t) - + 2*(a*b + c*d)*cos(t)*sin(t) - -Now apply the double angle formulae (A) to (C) from above: - - r^2(t) = (a^2 + b^2 + c^2 + d^2)/2 - + (a^2 - b^2 + c^2 - d^2)*cos(2*t)/2 - + (a*b + c*d)*sin(2*t) - = f + g*cos(u) + h*sin(u) - -Where - - f = (a^2 + b^2 + c^2 + d^2)/2 - g = (a^2 - b^2 + c^2 - d^2)/2 - h = (a*b + c*d) - u = 2*t - -It is clear that MAX[ |X'| ] = sqrt(MAX[ r^2 ]). Here we determine MAX[ r^2 ] -using (D) from above: - - MAX[ r^2 ] = f + sqrt(g^2 + h^2) - -And finally - - MAX[ |X'| ] = sqrt( f + sqrt(g^2 + h^2) ) - -Which is the solution to this problem. - - -Walter Brisken -2004/10/08 - -(Note that the minor axis length is at the minimum of the above solution, -which is just sqrt (f - sqrt (g^2 + h^2)) given the symmetry of (D)). - -Now to compute how many sides to use for the pen formed by -a regular polygon. - -Set - - M = major axis length (computed by above formula) - m = minor axis length (computed by above formula) - -Align 'M' along the X axis and 'm' along the Y axis and draw -an ellipse parameterized by angle 't': - - x = M cos t y = m sin t - -Perturb t by ± d and compute two new points (x+,y+), (x-,y-). -The distance from the average of these two points to (x,y) represents -the maximum error in approximating the ellipse with a polygon formed -from vertices 2∆ radians apart. - - x+ = M cos (t+∆) y+ = m sin (t+∆) - x- = M cos (t-∆) y- = m sin (t-∆) - -Now compute the approximation error, E: - - Ex = (x - (x+ + x-) / 2) - Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2) - = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) + - cos(t)cos(∆) - sin(t)sin(∆))/2) - = M(cos(t) - cos(t)cos(∆)) - = M cos(t) (1 - cos(∆)) - - Ey = y - (y+ - y-) / 2 - = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2 - = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) + - sin(t)cos(∆) - cos(t)sin(∆))/2) - = m (sin(t) - sin(t)cos(∆)) - = m sin(t) (1 - cos(∆)) - - E² = Ex² + Ey² - = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))² - = (1 - cos(∆))² (M² cos²(t) + m² sin²(t)) - = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t)) - = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m² - -Find the extremum by differentiation wrt t and setting that to zero - -∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t)) - - 0 = 2 cos (t) sin (t) - 0 = sin (2t) - t = nπ - -Which is to say that the maximum and minimum errors occur on the -axes of the ellipse at 0 and π radians: - - E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m² - = (1-cos(∆))² M² - E²(π) = (1-cos(∆))² m² - -maximum error = M (1-cos(∆)) -minimum error = m (1-cos(∆)) - -We must make maximum error ≤ tolerance, so compute the ∆ needed: - - tolerance = M (1-cos(∆)) - tolerance / M = 1 - cos (∆) - cos(∆) = 1 - tolerance/M - ∆ = acos (1 - tolerance / M); - -Remembering that ∆ is half of our angle between vertices, -the number of vertices is then - -vertices = ceil(2π/2∆). - = ceil(π/∆). - -Note that this also equation works for M == m (a circle) as it -doesn't matter where on the circle the error is computed. - -*/ - -static int -_cairo_pen_vertices_needed (double tolerance, - double radius, - cairo_matrix_t *matrix) -{ - double a = matrix->m[0][0], c = matrix->m[0][1]; - double b = matrix->m[1][0], d = matrix->m[1][1]; - - double i = a*a + c*c; - double j = b*b + d*d; - - double f = 0.5 * (i + j); - double g = 0.5 * (i - j); - double h = a*b + c*d; - - /* - * compute major and minor axes lengths for - * a pen with the specified radius - */ - - double major_axis = radius * sqrt (f + sqrt (g*g+h*h)); - - /* - * we don't need the minor axis length, which is - * double min = radius * sqrt (f - sqrt (g*g+h*h)); - */ - - /* - * compute number of vertices needed - */ - int num_vertices; - - /* Where tolerance / M is > 1, we use 4 points */ - if (tolerance >= major_axis) { - num_vertices = 4; - } else { - double delta = acos (1 - tolerance / major_axis); - num_vertices = ceil (M_PI / delta); - - /* number of vertices must be even */ - if (num_vertices % 2) - num_vertices++; - } - return num_vertices; -} - -static void -_cairo_pen_compute_slopes (cairo_pen_t *pen) -{ - int i, i_prev; - cairo_pen_vertex_t *prev, *v, *next; - - for (i=0, i_prev = pen->num_vertices - 1; - i < pen->num_vertices; - i_prev = i++) { - prev = &pen->vertices[i_prev]; - v = &pen->vertices[i]; - next = &pen->vertices[(i + 1) % pen->num_vertices]; - - _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); - _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); - } -} -/* - * Find active pen vertex for clockwise edge of stroke at the given slope. - * - * NOTE: The behavior of this function is sensitive to the sense of - * the inequality within _cairo_slope_clockwise/_cairo_slope_counter_clockwise. - * - * The issue is that the slope_ccw member of one pen vertex will be - * equivalent to the slope_cw member of the next pen vertex in a - * counterclockwise order. However, for this function, we care - * strongly about which vertex is returned. - */ -cairo_status_t -_cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen, - cairo_slope_t *slope, - int *active) -{ - int i; - - for (i=0; i < pen->num_vertices; i++) { - if (_cairo_slope_clockwise (slope, &pen->vertices[i].slope_ccw) - && _cairo_slope_counter_clockwise (slope, &pen->vertices[i].slope_cw)) - break; - } - - *active = i; - - return CAIRO_STATUS_SUCCESS; -} - -/* Find active pen vertex for counterclockwise edge of stroke at the given slope. - * - * NOTE: The behavior of this function is sensitive to the sense of - * the inequality within _cairo_slope_clockwise/_cairo_slope_counter_clockwise. - */ -cairo_status_t -_cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen, - cairo_slope_t *slope, - int *active) -{ - int i; - cairo_slope_t slope_reverse; - - slope_reverse = *slope; - slope_reverse.dx = -slope_reverse.dx; - slope_reverse.dy = -slope_reverse.dy; - - for (i=pen->num_vertices-1; i >= 0; i--) { - if (_cairo_slope_counter_clockwise (&pen->vertices[i].slope_ccw, &slope_reverse) - && _cairo_slope_clockwise (&pen->vertices[i].slope_cw, &slope_reverse)) - break; - } - - *active = i; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_pen_stroke_spline_half (cairo_pen_t *pen, - cairo_spline_t *spline, - cairo_direction_t dir, - cairo_polygon_t *polygon) -{ - int i; - cairo_status_t status; - int start, stop, step; - int active = 0; - cairo_point_t hull_point; - cairo_slope_t slope, initial_slope, final_slope; - cairo_point_t *point = spline->points; - int num_points = spline->num_points; - - if (dir == CAIRO_DIRECTION_FORWARD) { - start = 0; - stop = num_points; - step = 1; - initial_slope = spline->initial_slope; - final_slope = spline->final_slope; - } else { - start = num_points - 1; - stop = -1; - step = -1; - initial_slope = spline->final_slope; - initial_slope.dx = -initial_slope.dx; - initial_slope.dy = -initial_slope.dy; - final_slope = spline->initial_slope; - final_slope.dx = -final_slope.dx; - final_slope.dy = -final_slope.dy; - } - - _cairo_pen_find_active_cw_vertex_index (pen, &initial_slope, &active); - - i = start; - while (i != stop) { - hull_point.x = point[i].x + pen->vertices[active].point.x; - hull_point.y = point[i].y + pen->vertices[active].point.y; - status = _cairo_polygon_line_to (polygon, &hull_point); - if (status) - return status; - - if (i + step == stop) - slope = final_slope; - else - _cairo_slope_init (&slope, &point[i], &point[i+step]); - if (_cairo_slope_counter_clockwise (&slope, &pen->vertices[active].slope_ccw)) { - if (++active == pen->num_vertices) - active = 0; - } else if (_cairo_slope_clockwise (&slope, &pen->vertices[active].slope_cw)) { - if (--active == -1) - active = pen->num_vertices - 1; - } else { - i += step; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -/* Compute outline of a given spline using the pen. - The trapezoids needed to fill that outline will be added to traps -*/ -cairo_status_t -_cairo_pen_stroke_spline (cairo_pen_t *pen, - cairo_spline_t *spline, - double tolerance, - cairo_traps_t *traps) -{ - cairo_status_t status; - cairo_polygon_t polygon; - - /* If the line width is so small that the pen is reduced to a - single point, then we have nothing to do. */ - if (pen->num_vertices <= 1) - return CAIRO_STATUS_SUCCESS; - - _cairo_polygon_init (&polygon); - - status = _cairo_spline_decompose (spline, tolerance); - if (status) - return status; - - status = _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_FORWARD, &polygon); - if (status) - return status; - - status = _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_REVERSE, &polygon); - if (status) - return status; - - _cairo_polygon_close (&polygon); - _cairo_traps_tessellate_polygon (traps, &polygon, CAIRO_FILL_RULE_WINDING); - _cairo_polygon_fini (&polygon); - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo_png_surface.c b/src/cairo_png_surface.c deleted file mode 100644 index 1ae745cb5..000000000 --- a/src/cairo_png_surface.c +++ /dev/null @@ -1,425 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Olivier Andrieu <oliv__a@users.sourceforge.net> - * Carl D. Worth <cworth@cworth.org> - */ - -#include <png.h> - -#include "cairoint.h" -#include "cairo-png.h" - -static const cairo_surface_backend_t cairo_png_surface_backend; - -static cairo_int_status_t -_cairo_png_surface_copy_page (void *abstract_surface); - -void -cairo_set_target_png (cairo_t *cr, - FILE *file, - cairo_format_t format, - int width, - int height) -{ - cairo_surface_t *surface; - - surface = cairo_png_surface_create (file, format, - width, height); - - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - -typedef struct cairo_png_surface { - cairo_surface_t base; - - /* PNG-specific fields */ - cairo_image_surface_t *image; - FILE *file; - int copied; - - cairo_format_t format; - -} cairo_png_surface_t; - - -static void -_cairo_png_surface_erase (cairo_png_surface_t *surface); - -cairo_surface_t * -cairo_png_surface_create (FILE *file, - cairo_format_t format, - int width, - int height) -{ - cairo_png_surface_t *surface; - - surface = malloc (sizeof (cairo_png_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init (&surface->base, &cairo_png_surface_backend); - - surface->image = (cairo_image_surface_t *) - cairo_image_surface_create (format, width, height); - - if (surface->image == NULL) { - free (surface); - return NULL; - } - - _cairo_png_surface_erase (surface); - - surface->file = file; - surface->copied = 0; - - surface->format = format; - - return &surface->base; -} - - -static cairo_surface_t * -_cairo_png_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - return NULL; -} - -static void -unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) -{ - int i; - - for (i = 0; i < row_info->rowbytes; i += 4) { - unsigned char *b = &data[i]; - unsigned int pixel; - unsigned char alpha; - - memcpy (&pixel, b, sizeof (unsigned int)); - alpha = (pixel & 0xff000000) >> 24; - if (alpha == 0) { - b[0] = b[1] = b[2] = b[3] = 0; - } else { - b[0] = (((pixel & 0x0000ff) >> 0) * 255) / alpha; - b[1] = (((pixel & 0x00ff00) >> 8) * 255) / alpha; - b[2] = (((pixel & 0xff0000) >> 16) * 255) / alpha; - b[3] = alpha; - } - } -} - -static void -_cairo_png_surface_destroy (void *abstract_surface) -{ - cairo_png_surface_t *surface = abstract_surface; - - if (! surface->copied) - _cairo_png_surface_copy_page (surface); - - cairo_surface_destroy (&surface->image->base); - - free (surface); -} - -static void -_cairo_png_surface_erase (cairo_png_surface_t *surface) -{ - cairo_color_t transparent; - - _cairo_color_init (&transparent); - _cairo_color_set_rgb (&transparent, 0., 0., 0.); - _cairo_color_set_alpha (&transparent, 0.); - _cairo_surface_fill_rectangle (&surface->image->base, - CAIRO_OPERATOR_SRC, - &transparent, - 0, 0, - surface->image->width, - surface->image->height); -} - -static double -_cairo_png_surface_pixels_per_inch (void *abstract_surface) -{ - return 96.0; -} - -static cairo_status_t -_cairo_png_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_png_surface_t *surface = abstract_surface; - - *image_out = surface->image; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_png_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_png_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) -{ - cairo_png_surface_t *surface = abstract_surface; - - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->image->width; - image_rect->height = surface->image->height; - - *image_out = surface->image; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_png_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_png_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_png_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src, - cairo_pattern_t *mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_png_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_png_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_png_surface_copy_page (void *abstract_surface) -{ - int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_png_surface_t *surface = abstract_surface; - png_struct *png; - png_info *info; - png_byte **rows; - png_color_16 white; - int png_color_type; - int depth; - - rows = malloc (surface->image->height * sizeof(png_byte*)); - if (rows == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < surface->image->height; i++) - rows[i] = surface->image->data + i * surface->image->stride; - - png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } - - info = png_create_info_struct (png); - if (info == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL2; - } - - if (setjmp (png_jmpbuf (png))) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL2; - } - - png_init_io (png, surface->file); - - switch (surface->format) { - case CAIRO_FORMAT_ARGB32: - depth = 8; - png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; - break; - case CAIRO_FORMAT_RGB24: - depth = 8; - png_color_type = PNG_COLOR_TYPE_RGB; - break; - case CAIRO_FORMAT_A8: - depth = 8; - png_color_type = PNG_COLOR_TYPE_GRAY; - break; - case CAIRO_FORMAT_A1: - depth = 1; - png_color_type = PNG_COLOR_TYPE_GRAY; - break; - default: - status = CAIRO_STATUS_NULL_POINTER; - goto BAIL2; - } - - png_set_IHDR (png, info, - surface->image->width, - surface->image->height, depth, - png_color_type, - PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT); - - white.red = 0xff; - white.blue = 0xff; - white.green = 0xff; - png_set_bKGD (png, info, &white); - -/* XXX: Setting the time is interfereing with the image comparison - png_convert_from_time_t (&png_time, time (NULL)); - png_set_tIME (png, info, &png_time); -*/ - - png_set_write_user_transform_fn (png, unpremultiply_data); - if (surface->format == CAIRO_FORMAT_ARGB32 || surface->format == CAIRO_FORMAT_RGB24) - png_set_bgr (png); - if (surface->format == CAIRO_FORMAT_RGB24) - png_set_filler (png, 0, PNG_FILLER_AFTER); - - png_write_info (png, info); - png_write_image (png, rows); - png_write_end (png, info); - - surface->copied = 1; - -BAIL2: - png_destroy_write_struct (&png, &info); -BAIL1: - free (rows); - - return status; -} - -static cairo_int_status_t -_cairo_png_surface_show_page (void *abstract_surface) -{ - cairo_int_status_t status; - cairo_png_surface_t *surface = abstract_surface; - - status = _cairo_png_surface_copy_page (surface); - if (status) - return status; - - _cairo_png_surface_erase (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_png_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - cairo_png_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_clip_region (surface->image, region); -} - -static const cairo_surface_backend_t cairo_png_surface_backend = { - _cairo_png_surface_create_similar, - _cairo_png_surface_destroy, - _cairo_png_surface_pixels_per_inch, - _cairo_png_surface_acquire_source_image, - _cairo_png_surface_release_source_image, - _cairo_png_surface_acquire_dest_image, - _cairo_png_surface_release_dest_image, - _cairo_png_surface_clone_similar, - _cairo_png_surface_composite, - _cairo_png_surface_fill_rectangles, - _cairo_png_surface_composite_trapezoids, - _cairo_png_surface_copy_page, - _cairo_png_surface_show_page, - _cairo_png_surface_set_clip_region, - NULL /* show_glyphs */ -}; diff --git a/src/cairo_polygon.c b/src/cairo_polygon.c deleted file mode 100644 index 59c615da2..000000000 --- a/src/cairo_polygon.c +++ /dev/null @@ -1,172 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include <stdlib.h> -#include "cairoint.h" - -/* private functions */ - -static cairo_status_t -_cairo_polygon_grow_by (cairo_polygon_t *polygon, int additional); - -void -_cairo_polygon_init (cairo_polygon_t *polygon) -{ - polygon->num_edges = 0; - - polygon->edges_size = 0; - polygon->edges = NULL; - - polygon->has_current_point = 0; -} - -void -_cairo_polygon_fini (cairo_polygon_t *polygon) -{ - if (polygon->edges_size) { - free (polygon->edges); - polygon->edges = NULL; - polygon->edges_size = 0; - polygon->num_edges = 0; - } - - polygon->has_current_point = 0; -} - -static cairo_status_t -_cairo_polygon_grow_by (cairo_polygon_t *polygon, int additional) -{ - cairo_edge_t *new_edges; - int old_size = polygon->edges_size; - int new_size = polygon->num_edges + additional; - - if (new_size <= polygon->edges_size) { - return CAIRO_STATUS_SUCCESS; - } - - polygon->edges_size = new_size; - new_edges = realloc (polygon->edges, polygon->edges_size * sizeof (cairo_edge_t)); - - if (new_edges == NULL) { - polygon->edges_size = old_size; - return CAIRO_STATUS_NO_MEMORY; - } - - polygon->edges = new_edges; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_polygon_add_edge (cairo_polygon_t *polygon, cairo_point_t *p1, cairo_point_t *p2) -{ - cairo_status_t status; - cairo_edge_t *edge; - - /* drop horizontal edges */ - if (p1->y == p2->y) { - goto DONE; - } - - if (polygon->num_edges >= polygon->edges_size) { - int additional = polygon->edges_size ? polygon->edges_size : 16; - status = _cairo_polygon_grow_by (polygon, additional); - if (status) { - return status; - } - } - - edge = &polygon->edges[polygon->num_edges]; - if (p1->y < p2->y) { - edge->edge.p1 = *p1; - edge->edge.p2 = *p2; - edge->clockWise = 1; - } else { - edge->edge.p1 = *p2; - edge->edge.p2 = *p1; - edge->clockWise = 0; - } - - polygon->num_edges++; - - DONE: - _cairo_polygon_move_to (polygon, p2); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_polygon_move_to (cairo_polygon_t *polygon, cairo_point_t *point) -{ - if (! polygon->has_current_point) - polygon->first_point = *point; - polygon->current_point = *point; - polygon->has_current_point = 1; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_polygon_line_to (cairo_polygon_t *polygon, cairo_point_t *point) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (polygon->has_current_point) { - status = _cairo_polygon_add_edge (polygon, &polygon->current_point, point); - } else { - _cairo_polygon_move_to (polygon, point); - } - - return status; -} - -cairo_status_t -_cairo_polygon_close (cairo_polygon_t *polygon) -{ - cairo_status_t status; - - if (polygon->has_current_point) { - status = _cairo_polygon_add_edge (polygon, - &polygon->current_point, - &polygon->first_point); - if (status) - return status; - - polygon->has_current_point = 0; - } - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo_ps_surface.c b/src/cairo_ps_surface.c deleted file mode 100644 index 4a45fc679..000000000 --- a/src/cairo_ps_surface.c +++ /dev/null @@ -1,443 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2003 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. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" -#include "cairo-ps.h" - -#include <time.h> -#include <zlib.h> - -static const cairo_surface_backend_t cairo_ps_surface_backend; - -/** - * cairo_set_target_ps: - * @cr: a #cairo_t - * @file: an open, writeable file - * @width_inches: width of the output page, in inches - * @height_inches: height of the output page, in inches - * @x_pixels_per_inch: X resolution to use for image fallbacks; - * not all Cairo drawing can be represented in a postscript - * file, so Cairo will write out images for some portions - * of the output. - * @y_pixels_per_inch: Y resolution to use for image fallbacks. - * - * Directs output for a #cairo_t to a postscript file. The file must - * be kept open until the #cairo_t is destroyed or set to have a - * different target, and then must be closed by the application. - **/ -void -cairo_set_target_ps (cairo_t *cr, - FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_surface_t *surface; - - surface = cairo_ps_surface_create (file, - width_inches, height_inches, - x_pixels_per_inch, y_pixels_per_inch); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - -typedef struct cairo_ps_surface { - cairo_surface_t base; - - /* PS-specific fields */ - FILE *file; - - double width_inches; - double height_inches; - double x_ppi; - double y_ppi; - - int pages; - - cairo_image_surface_t *image; -} cairo_ps_surface_t; - -static void -_cairo_ps_surface_erase (cairo_ps_surface_t *surface); - -cairo_surface_t * -cairo_ps_surface_create (FILE *file, - double width_inches, - double height_inches, - double x_pixels_per_inch, - double y_pixels_per_inch) -{ - cairo_ps_surface_t *surface; - int width, height; - time_t now = time (0); - - surface = malloc (sizeof (cairo_ps_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init (&surface->base, &cairo_ps_surface_backend); - - surface->file = file; - - surface->width_inches = width_inches; - surface->height_inches = height_inches; - surface->x_ppi = x_pixels_per_inch; - surface->y_ppi = x_pixels_per_inch; - - surface->pages = 0; - - width = (int) (x_pixels_per_inch * width_inches + 1.0); - height = (int) (y_pixels_per_inch * height_inches + 1.0); - - surface->image = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - if (surface->image == NULL) { - free (surface); - return NULL; - } - - _cairo_ps_surface_erase (surface); - - /* Document header */ - fprintf (file, - "%%!PS-Adobe-3.0\n" - "%%%%Creator: Cairo (http://cairographics.org)\n"); - fprintf (file, - "%%%%CreationDate: %s", - ctime (&now)); - fprintf (file, - "%%%%BoundingBox: %d %d %d %d\n", - 0, 0, (int) (surface->width_inches * 72.0), (int) (surface->height_inches * 72.0)); - /* The "/FlateDecode filter" currently used is a feature of LanguageLevel 3 */ - fprintf (file, - "%%%%DocumentData: Clean7Bit\n" - "%%%%LanguageLevel: 3\n"); - fprintf (file, - "%%%%Orientation: Portrait\n" - "%%%%EndComments\n"); - - return &surface->base; -} - -static cairo_surface_t * -_cairo_ps_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - return NULL; -} - -static void -_cairo_ps_surface_destroy (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - - /* Document footer */ - fprintf (surface->file, "%%%%EOF\n"); - - cairo_surface_destroy (&surface->image->base); - - free (surface); -} - -static void -_cairo_ps_surface_erase (cairo_ps_surface_t *surface) -{ - cairo_color_t transparent; - - _cairo_color_init (&transparent); - _cairo_color_set_rgb (&transparent, 0., 0., 0.); - _cairo_color_set_alpha (&transparent, 0.); - _cairo_surface_fill_rectangle (&surface->image->base, - CAIRO_OPERATOR_SRC, - &transparent, - 0, 0, - surface->image->width, - surface->image->height); -} - -/* XXX: We should re-work this interface to return both X/Y ppi values. */ -static double -_cairo_ps_surface_pixels_per_inch (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - - return surface->y_ppi; -} - -static cairo_status_t -_cairo_ps_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_ps_surface_t *surface = abstract_surface; - - *image_out = surface->image; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_ps_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_ps_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) -{ - cairo_ps_surface_t *surface = abstract_surface; - - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->image->width; - image_rect->height = surface->image->height; - - *image_out = surface->image; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_ps_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ -} - -static cairo_status_t -_cairo_ps_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src, - cairo_pattern_t *mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *generic_src, - void *abstract_dst, - 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) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_ps_surface_copy_page (void *abstract_surface) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_ps_surface_t *surface = abstract_surface; - int width = surface->image->width; - int height = surface->image->height; - FILE *file = surface->file; - - int i, x, y; - - cairo_solid_pattern_t white_pattern; - char *rgb, *compressed; - long rgb_size, compressed_size; - - rgb_size = 3 * width * height; - rgb = malloc (rgb_size); - if (rgb == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - - compressed_size = (int) (1.0 + 1.1 * rgb_size); - compressed = malloc (compressed_size); - if (compressed == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } - - /* PostScript can not represent the alpha channel, so we blend the - current image over a white RGB surface to eliminate it. */ - - _cairo_pattern_init_solid (&white_pattern, 1.0, 1.0, 1.0); - - _cairo_surface_composite (CAIRO_OPERATOR_OVER_REVERSE, - &white_pattern.base, - NULL, - &surface->image->base, - 0, 0, - 0, 0, - 0, 0, - width, height); - - _cairo_pattern_fini (&white_pattern.base); - - i = 0; - for (y = 0; y < height; y++) { - pixman_bits_t *pixel = (pixman_bits_t *) (surface->image->data + y * surface->image->stride); - for (x = 0; x < width; x++, pixel++) { - rgb[i++] = (*pixel & 0x00ff0000) >> 16; - rgb[i++] = (*pixel & 0x0000ff00) >> 8; - rgb[i++] = (*pixel & 0x000000ff) >> 0; - } - } - - compress (compressed, &compressed_size, rgb, rgb_size); - - /* Page header */ - fprintf (file, "%%%%Page: %d\n", ++surface->pages); - - fprintf (file, "gsave\n"); - - /* Image header goop */ - fprintf (file, "%g %g translate\n", 0.0, surface->height_inches * 72.0); - fprintf (file, "%g %g scale\n", 72.0 / surface->x_ppi, 72.0 / surface->y_ppi); - fprintf (file, "/DeviceRGB setcolorspace\n"); - fprintf (file, "<<\n"); - fprintf (file, " /ImageType 1\n"); - fprintf (file, " /Width %d\n", width); - fprintf (file, " /Height %d\n", height); - fprintf (file, " /BitsPerComponent 8\n"); - fprintf (file, " /Decode [ 0 1 0 1 0 1 ]\n"); - fprintf (file, " /DataSource currentfile /FlateDecode filter\n"); - fprintf (file, " /ImageMatrix [ 1 0 0 -1 0 1 ]\n"); - fprintf (file, ">>\n"); - fprintf (file, "image\n"); - - /* Compressed image data */ - fwrite (compressed, 1, compressed_size, file); - - fprintf (file, "showpage\n"); - - fprintf (file, "grestore\n"); - - /* Page footer */ - fprintf (file, "%%%%EndPage\n"); - - free (compressed); - BAIL1: - free (rgb); - BAIL0: - return status; -} - -static cairo_int_status_t -_cairo_ps_surface_show_page (void *abstract_surface) -{ - cairo_int_status_t status; - cairo_ps_surface_t *surface = abstract_surface; - - status = _cairo_ps_surface_copy_page (surface); - if (status) - return status; - - _cairo_ps_surface_erase (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_ps_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - cairo_ps_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_clip_region (surface->image, region); -} - -static const cairo_surface_backend_t cairo_ps_surface_backend = { - _cairo_ps_surface_create_similar, - _cairo_ps_surface_destroy, - _cairo_ps_surface_pixels_per_inch, - _cairo_ps_surface_acquire_source_image, - _cairo_ps_surface_release_source_image, - _cairo_ps_surface_acquire_dest_image, - _cairo_ps_surface_release_dest_image, - _cairo_ps_surface_clone_similar, - _cairo_ps_surface_composite, - _cairo_ps_surface_fill_rectangles, - _cairo_ps_surface_composite_trapezoids, - _cairo_ps_surface_copy_page, - _cairo_ps_surface_show_page, - _cairo_ps_surface_set_clip_region, - NULL /* show_glyphs */ -}; diff --git a/src/cairo_quartz_surface.c b/src/cairo_quartz_surface.c deleted file mode 100644 index 01b345cdc..000000000 --- a/src/cairo_quartz_surface.c +++ /dev/null @@ -1,392 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 Calum Robinson - * - * 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. - * - * The Initial Developer of the Original Code is Calum Robinson - * - * Contributor(s): - * Calum Robinson <calumr@mac.com> - */ - -#include "cairoint.h" -#include "cairo-quartz.h" - -#pragma mark Types - -typedef struct cairo_quartz_surface { - cairo_surface_t base; - - CGContextRef context; - - int width; - int height; - - cairo_image_surface_t *image; - - CGImageRef cgImage; -} cairo_quartz_surface_t; - - - - - -#pragma mark Private functions - - - - -void ImageDataReleaseFunc(void *info, const void *data, size_t size) -{ - if (data != NULL) - { - free((void *)data); - } -} - - - - -#pragma mark Public functions - - - - - -void -cairo_set_target_quartz_context( cairo_t *cr, - CGContextRef context, - int width, - int height) -{ - cairo_surface_t *surface; - - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_quartz_surface_create(context, width, height); - if (surface == NULL) - { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface(cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy(surface); -} - - -static cairo_surface_t * -_cairo_quartz_surface_create_similar( void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - return NULL; -} - - -static void -_cairo_quartz_surface_destroy(void *abstract_surface) -{ - cairo_quartz_surface_t *surface = abstract_surface; - - - if (surface->cgImage) - { - CGImageRelease(surface->cgImage); - } - - - free(surface); -} - - -static double -_cairo_quartz_surface_pixels_per_inch(void *abstract_surface) -{ - - - // TODO - get this from CGDirectDisplay somehow? - return 96.0; -} - - -static cairo_image_surface_t * -_cairo_quartz_surface_get_image(void *abstract_surface) -{ - cairo_quartz_surface_t *surface = abstract_surface; - CGColorSpaceRef colorSpace; - void *imageData; - UInt32 imageDataSize, rowBytes; - CGDataProviderRef dataProvider; - - - // We keep a cached (cairo_image_surface_t *) in the cairo_quartz_surface_t - // struct. If the window is ever drawn to without going through Cairo, then - // we would need to refetch the pixel data from the window into the cached - // image surface. - if (surface->image) - { - cairo_surface_reference(&surface->image->base); - - return surface->image; - } - - colorSpace = CGColorSpaceCreateDeviceRGB(); - - - rowBytes = surface->width * 4; - imageDataSize = rowBytes * surface->height; - imageData = malloc(imageDataSize); - - dataProvider = CGDataProviderCreateWithData(NULL, imageData, imageDataSize, ImageDataReleaseFunc); - - surface->cgImage = CGImageCreate( surface->width, - surface->height, - 8, - 32, - rowBytes, - colorSpace, - kCGImageAlphaPremultipliedFirst, - dataProvider, - NULL, - false, - kCGRenderingIntentDefault); - - - CGColorSpaceRelease(colorSpace); - CGDataProviderRelease(dataProvider); - - - surface->image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data( imageData, - CAIRO_FORMAT_ARGB32, - surface->width, - surface->height, - rowBytes); - - - // Set the image surface Cairo state to match our own. - _cairo_image_surface_set_repeat(surface->image, surface->base.repeat); - _cairo_image_surface_set_matrix(surface->image, &(surface->base.matrix)); - - - return surface->image; -} - - -static cairo_status_t -_cairo_quartz_surface_set_image( void *abstract_surface, - cairo_image_surface_t *image) -{ - cairo_quartz_surface_t *surface = abstract_surface; - cairo_status_t status; - - - if (surface->image == image) - { - CGRect rect; - - - rect = CGRectMake(0, 0, surface->width, surface->height); - - CGContextDrawImage(surface->context, rect, surface->cgImage); - - - status = CAIRO_STATUS_SUCCESS; - } - else - { - // TODO - set_image from something other than what we returned from get_image - status = CAIRO_STATUS_NO_TARGET_SURFACE; - } - - - return status; -} - - -static cairo_status_t -_cairo_quartz_surface_set_matrix(void *abstract_surface, cairo_matrix_t *matrix) -{ - cairo_quartz_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_matrix(surface->image, matrix); -} - - -static cairo_status_t -_cairo_quartz_surface_set_filter(void *abstract_surface, cairo_filter_t filter) -{ - cairo_quartz_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_filter(surface->image, filter); -} - - -static cairo_status_t -_cairo_quartz_surface_set_repeat(void *abstract_surface, int repeat) -{ - cairo_quartz_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_repeat(surface->image, repeat); -} - - -static cairo_int_status_t -_cairo_quartz_surface_composite( cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_quartz_surface_fill_rectangles( void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_quartz_surface_composite_trapezoids( cairo_operator_t operator, - cairo_surface_t *generic_src, - void *abstract_dst, - int xSrc, - int ySrc, - cairo_trapezoid_t *traps, - int num_traps) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_quartz_surface_copy_page(void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_quartz_surface_show_page(void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_quartz_surface_set_clip_region( void *abstract_surface, - pixman_region16_t *region) -{ - cairo_quartz_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_clip_region(surface->image, region); -} - - -static cairo_int_status_t -_cairo_quartz_surface_create_pattern( void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static const struct _cairo_surface_backend cairo_quartz_surface_backend = { - _cairo_quartz_surface_create_similar, - _cairo_quartz_surface_destroy, - _cairo_quartz_surface_pixels_per_inch, - _cairo_quartz_surface_get_image, - _cairo_quartz_surface_set_image, - _cairo_quartz_surface_set_matrix, - _cairo_quartz_surface_set_filter, - _cairo_quartz_surface_set_repeat, - _cairo_quartz_surface_composite, - _cairo_quartz_surface_fill_rectangles, - _cairo_quartz_surface_composite_trapezoids, - _cairo_quartz_surface_copy_page, - _cairo_quartz_surface_show_page, - _cairo_quartz_surface_set_clip_region, - _cairo_quartz_surface_create_pattern -}; - - -cairo_surface_t * -cairo_quartz_surface_create( CGContextRef context, - int width, - int height) -{ - cairo_quartz_surface_t *surface; - - - surface = malloc(sizeof(cairo_quartz_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend); - - - surface->context = context; - - surface->width = width; - surface->height = height; - - surface->image = NULL; - - surface->cgImage = NULL; - - - // Set up the image surface which Cairo draws into and we blit to & from. - surface->image = _cairo_quartz_surface_get_image(surface); - - - return (cairo_surface_t *)surface; -} - - -DEPRECATE (cairo_surface_create_for_drawable, cairo_quartz_surface_create); diff --git a/src/cairo_slope.c b/src/cairo_slope.c deleted file mode 100644 index a2edec038..000000000 --- a/src/cairo_slope.c +++ /dev/null @@ -1,103 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -void -_cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b) -{ - slope->dx = b->x - a->x; - slope->dy = b->y - a->y; -} - -/* Compare two slopes. Slope angles begin at 0 in the direction of the - positive X axis and increase in the direction of the positive Y - axis. - - WARNING: This function only gives correct results if the angular - difference between a and b is less than PI. - - < 0 => a less positive than b - == 0 => a equal to be - > 0 => a more positive than b -*/ -int -_cairo_slope_compare (cairo_slope_t *a, cairo_slope_t *b) -{ - cairo_fixed_48_16_t diff; - - diff = ((cairo_fixed_48_16_t) a->dy * (cairo_fixed_48_16_t) b->dx - - (cairo_fixed_48_16_t) b->dy * (cairo_fixed_48_16_t) a->dx); - - if (diff > 0) - return 1; - if (diff < 0) - return -1; - - if (a->dx == 0 && a->dy == 0) - return 1; - if (b->dx == 0 && b->dy ==0) - return -1; - - return 0; -} - -/* XXX: It might be cleaner to move away from usage of - _cairo_slope_clockwise/_cairo_slope_counter_clockwise in favor of - directly using _cairo_slope_compare. -*/ - -/* Is a clockwise of b? - * - * NOTE: The strict equality here is not significant in and of itself, - * but there are functions up above that are sensitive to it, - * (cf. _cairo_pen_find_active_cw_vertex_index). - */ -int -_cairo_slope_clockwise (cairo_slope_t *a, cairo_slope_t *b) -{ - return _cairo_slope_compare (a, b) < 0; -} - -int -_cairo_slope_counter_clockwise (cairo_slope_t *a, cairo_slope_t *b) -{ - return ! _cairo_slope_clockwise (a, b); -} - - - - diff --git a/src/cairo_spline.c b/src/cairo_spline.c deleted file mode 100644 index 5119a8e2b..000000000 --- a/src/cairo_spline.c +++ /dev/null @@ -1,288 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" - -static cairo_status_t -_cairo_spline_grow_by (cairo_spline_t *spline, int additional); - -static cairo_status_t -_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point); - -static void -_lerp_half (cairo_point_t *a, cairo_point_t *b, cairo_point_t *result); - -static void -_de_casteljau (cairo_spline_t *spline, cairo_spline_t *s1, cairo_spline_t *s2); - -static double -_cairo_spline_error_squared (cairo_spline_t *spline); - -static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_t *spline, double tolerance_squared, cairo_spline_t *result); - -cairo_int_status_t -_cairo_spline_init (cairo_spline_t *spline, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) -{ - spline->a = *a; - spline->b = *b; - spline->c = *c; - spline->d = *d; - - if (a->x != b->x || a->y != b->y) { - _cairo_slope_init (&spline->initial_slope, &spline->a, &spline->b); - } else if (a->x != c->x || a->y != c->y) { - _cairo_slope_init (&spline->initial_slope, &spline->a, &spline->c); - } else if (a->x != d->x || a->y != d->y) { - _cairo_slope_init (&spline->initial_slope, &spline->a, &spline->d); - } else { - return CAIRO_INT_STATUS_DEGENERATE; - } - - if (c->x != d->x || c->y != d->y) { - _cairo_slope_init (&spline->final_slope, &spline->c, &spline->d); - } else if (b->x != d->x || b->y != d->y) { - _cairo_slope_init (&spline->final_slope, &spline->b, &spline->d); - } else { - _cairo_slope_init (&spline->final_slope, &spline->a, &spline->d); - } - - spline->num_points = 0; - spline->points_size = 0; - spline->points = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_spline_fini (cairo_spline_t *spline) -{ - spline->num_points = 0; - spline->points_size = 0; - free (spline->points); - spline->points = NULL; -} - -static cairo_status_t -_cairo_spline_grow_by (cairo_spline_t *spline, int additional) -{ - cairo_point_t *new_points; - int old_size = spline->points_size; - int new_size = spline->num_points + additional; - - if (new_size <= spline->points_size) - return CAIRO_STATUS_SUCCESS; - - spline->points_size = new_size; - new_points = realloc (spline->points, spline->points_size * sizeof (cairo_point_t)); - - if (new_points == NULL) { - spline->points_size = old_size; - return CAIRO_STATUS_NO_MEMORY; - } - - spline->points = new_points; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point) -{ - cairo_status_t status; - cairo_point_t *prev; - - if (spline->num_points) { - prev = &spline->points[spline->num_points - 1]; - if (prev->x == point->x && prev->y == point->y) - return CAIRO_STATUS_SUCCESS; - } - - if (spline->num_points >= spline->points_size) { - int additional = spline->points_size ? spline->points_size : 32; - status = _cairo_spline_grow_by (spline, additional); - if (status) - return status; - } - - spline->points[spline->num_points] = *point; - spline->num_points++; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_lerp_half (cairo_point_t *a, cairo_point_t *b, cairo_point_t *result) -{ - result->x = a->x + ((b->x - a->x) >> 1); - result->y = a->y + ((b->y - a->y) >> 1); -} - -static void -_de_casteljau (cairo_spline_t *spline, cairo_spline_t *s1, cairo_spline_t *s2) -{ - cairo_point_t ab, bc, cd; - cairo_point_t abbc, bccd; - cairo_point_t final; - - _lerp_half (&spline->a, &spline->b, &ab); - _lerp_half (&spline->b, &spline->c, &bc); - _lerp_half (&spline->c, &spline->d, &cd); - _lerp_half (&ab, &bc, &abbc); - _lerp_half (&bc, &cd, &bccd); - _lerp_half (&abbc, &bccd, &final); - - s1->a = spline->a; - s1->b = ab; - s1->c = abbc; - s1->d = final; - - s2->a = final; - s2->b = bccd; - s2->c = cd; - s2->d = spline->d; -} - -static double -_PointDistanceSquaredToPoint (cairo_point_t *a, cairo_point_t *b) -{ - double dx = _cairo_fixed_to_double (b->x - a->x); - double dy = _cairo_fixed_to_double (b->y - a->y); - - return dx*dx + dy*dy; -} - -static double -_PointDistanceSquaredToSegment (cairo_point_t *p, cairo_point_t *p1, cairo_point_t *p2) -{ - double u; - double dx, dy; - double pdx, pdy; - cairo_point_t px; - - /* intersection point (px): - - px = p1 + u(p2 - p1) - (p - px) . (p2 - p1) = 0 - - Thus: - - u = ((p - p1) . (p2 - p1)) / (||(p2 - p1)|| ^ 2); - */ - - dx = _cairo_fixed_to_double (p2->x - p1->x); - dy = _cairo_fixed_to_double (p2->y - p1->y); - - if (dx == 0 && dy == 0) - return _PointDistanceSquaredToPoint (p, p1); - - pdx = _cairo_fixed_to_double (p->x - p1->x); - pdy = _cairo_fixed_to_double (p->y - p1->y); - - u = (pdx * dx + pdy * dy) / (dx*dx + dy*dy); - - if (u <= 0) - return _PointDistanceSquaredToPoint (p, p1); - else if (u >= 1) - return _PointDistanceSquaredToPoint (p, p2); - - px.x = p1->x + u * (p2->x - p1->x); - px.y = p1->y + u * (p2->y - p1->y); - - return _PointDistanceSquaredToPoint (p, &px); -} - -/* Return an upper bound on the error (squared) that could result from approximating - a spline as a line segment connecting the two endpoints */ -static double -_cairo_spline_error_squared (cairo_spline_t *spline) -{ - double berr, cerr; - - berr = _PointDistanceSquaredToSegment (&spline->b, &spline->a, &spline->d); - cerr = _PointDistanceSquaredToSegment (&spline->c, &spline->a, &spline->d); - - if (berr > cerr) - return berr; - else - return cerr; -} - -static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_t *spline, double tolerance_squared, cairo_spline_t *result) -{ - cairo_status_t status; - cairo_spline_t s1, s2; - - if (_cairo_spline_error_squared (spline) < tolerance_squared) { - return _cairo_spline_add_point (result, &spline->a); - } - - _de_casteljau (spline, &s1, &s2); - - status = _cairo_spline_decompose_into (&s1, tolerance_squared, result); - if (status) - return status; - - status = _cairo_spline_decompose_into (&s2, tolerance_squared, result); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_spline_decompose (cairo_spline_t *spline, double tolerance) -{ - cairo_status_t status; - - if (spline->points_size) { - _cairo_spline_fini (spline); - } - - status = _cairo_spline_decompose_into (spline, tolerance * tolerance, spline); - if (status) - return status; - - status = _cairo_spline_add_point (spline, &spline->d); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - diff --git a/src/cairo_surface.c b/src/cairo_surface.c deleted file mode 100644 index 330d58b1e..000000000 --- a/src/cairo_surface.c +++ /dev/null @@ -1,708 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include <stdlib.h> - -#include "cairoint.h" - -void -_cairo_surface_init (cairo_surface_t *surface, - const cairo_surface_backend_t *backend) -{ - surface->backend = backend; - - surface->ref_count = 1; - - _cairo_matrix_init (&surface->matrix); - surface->filter = CAIRO_FILTER_NEAREST; - surface->repeat = 0; -} - -cairo_surface_t * -cairo_surface_create_for_image (char *data, - cairo_format_t format, - int width, - int height, - int stride) -{ - return cairo_image_surface_create_for_data (data, format, width, height, stride); -} -slim_hidden_def(cairo_surface_create_for_image); - -cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_format_t format, - int drawable, - int width, - int height) -{ - if (other == NULL) - return NULL; - - return other->backend->create_similar (other, format, drawable, - width, height); -} - -cairo_surface_t * -cairo_surface_create_similar (cairo_surface_t *other, - cairo_format_t format, - int width, - int height) -{ - cairo_color_t empty; - - if (other == NULL) - return NULL; - - _cairo_color_init (&empty); - _cairo_color_set_rgb (&empty, 0., 0., 0.); - _cairo_color_set_alpha (&empty, 0.); - - return _cairo_surface_create_similar_solid (other, format, width, height, &empty); -} - -cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_format_t format, - int width, - int height, - cairo_color_t *color) -{ - cairo_status_t status; - cairo_surface_t *surface; - - surface = _cairo_surface_create_similar_scratch (other, format, 1, - width, height); - - if (surface == NULL) - surface = cairo_image_surface_create (format, width, height); - - status = _cairo_surface_fill_rectangle (surface, - CAIRO_OPERATOR_SRC, color, - 0, 0, width, height); - if (status) { - cairo_surface_destroy (surface); - return NULL; - } - - return surface; -} - -void -cairo_surface_reference (cairo_surface_t *surface) -{ - if (surface == NULL) - return; - - surface->ref_count++; -} - -void -cairo_surface_destroy (cairo_surface_t *surface) -{ - if (surface == NULL) - return; - - surface->ref_count--; - if (surface->ref_count) - return; - - if (surface->backend->destroy) - surface->backend->destroy (surface); -} -slim_hidden_def(cairo_surface_destroy); - -double -_cairo_surface_pixels_per_inch (cairo_surface_t *surface) -{ - return surface->backend->pixels_per_inch (surface); -} - -/** - * _cairo_surface_acquire_source_image: - * @surface: a #cairo_surface_t - * @image_out: location to store a pointer to an image surface that includes at least - * the intersection of @interest_rect with the visible area of @surface. - * This surface could be @surface itself, a surface held internal to @surface, - * or it could be a new surface with a copy of the relevant portion of @surface. - * @image_extra: location to store image specific backend data - * - * Gets an image surface to use when drawing as a fallback when drawing with - * @surface as a source. _cairo_surface_release_source_image() must be called - * when finished. - * - * Return value: %CAIRO_STATUS_SUCCESS if a an image was stored in @image_out. - * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified - * surface. Or %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_surface_acquire_source_image (cairo_surface_t *surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - return surface->backend->acquire_source_image (surface, image_out, image_extra); -} - -/** - * _cairo_surface_release_source_image: - * @surface: a #cairo_surface_t - * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() - * - * Releases any resources obtained with _cairo_surface_acquire_source_image() - **/ -cairo_private void -_cairo_surface_release_source_image (cairo_surface_t *surface, - cairo_image_surface_t *image, - void *image_extra) -{ - surface->backend->release_source_image (surface, image, image_extra); -} - -/** - * _cairo_surface_acquire_dest_image: - * @surface: a #cairo_surface_t - * @interest_rect: area of @surface for which fallback drawing is being done. - * A value of %NULL indicates that the entire surface is desired. - * @image_out: location to store a pointer to an image surface that includes at least - * the intersection of @interest_rect with the visible area of @surface. - * This surface could be @surface itself, a surface held internal to @surface, - * or it could be a new surface with a copy of the relevant portion of @surface. - * @image_rect: location to store area of the original surface occupied - * by the surface stored in @image. - * @image_extra: location to store image specific backend data - * - * Retrieves a local image for a surface for implementing a fallback drawing - * operation. After calling this function, the implementation of the fallback - * drawing operation draws the primitive to the surface stored in @image_out - * then calls _cairo_surface_release_dest_fallback(), - * which, if a temporary surface was created, copies the bits back to the - * main surface and frees the temporary surface. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. - * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that - * the backend can't draw with fallbacks. It's possible for the routine - * to store NULL in @local_out and return %CAIRO_STATUS_SUCCESS; - * that indicates that no part of @interest_rect is visible, so no drawing - * is necessary. _cairo_surface_release_dest_fallback() should not be called in that - * case. - **/ -cairo_status_t -_cairo_surface_acquire_dest_image (cairo_surface_t *surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) -{ - return surface->backend->acquire_dest_image (surface, interest_rect, - image_out, image_rect, image_extra); -} - -/** - * _cairo_surface_end_fallback: - * @surface: a #cairo_surface_t - * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() - * @image: same as returned from the matching _cairo_surface_acquire_dest_image() - * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() - * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() - * - * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if - * necessary, copying the image from @image back to @surface and freeing any - * resources that were allocated. - **/ -void -_cairo_surface_release_dest_image (cairo_surface_t *surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ - surface->backend->release_dest_image (surface, interest_rect, - image, image_rect, image_extra); -} - -/** - * _cairo_surface_clone_similar: - * @surface: a #cairo_surface_t - * @src: the source image - * @clone_out: location to store a surface compatible with @surface - * and with contents identical to @src. The caller must call - * cairo_surface_destroy() on the result. - * - * Creates a surface with contents identical to @src but that - * can be used efficiently with @surface. If @surface and @src are - * already compatible then it may return a new reference to @src. - * - * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored - * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another - * error like %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -_cairo_surface_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - cairo_status_t status; - cairo_image_surface_t *image; - void *image_extra; - - status = surface->backend->clone_similar (surface, src, clone_out); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_surface_acquire_source_image (src, &image, &image_extra); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - status = surface->backend->clone_similar (surface, &image->base, clone_out); - - /* If the above failed point, we could implement a full fallback - * using acquire_dest_image, but that's going to be very - * inefficient compared to a backend-specific implementation of - * clone_similar() with an image source. So we don't bother - */ - - _cairo_surface_release_source_image (src, image, image_extra); - return status; -} - -cairo_status_t -cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - return cairo_matrix_copy (&surface->matrix, matrix); -} -slim_hidden_def(cairo_surface_set_matrix); - -cairo_status_t -cairo_surface_get_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - return cairo_matrix_copy (matrix, &surface->matrix); -} -slim_hidden_def(cairo_surface_get_matrix); - -cairo_status_t -cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - surface->filter = filter; - return CAIRO_STATUS_SUCCESS; -} - -cairo_filter_t -cairo_surface_get_filter (cairo_surface_t *surface) -{ - return surface->filter; -} - -/* XXX: NYI -cairo_status_t -cairo_surface_clip_rectangle (cairo_surface_t *surface, - int x, int y, - int width, int height) -{ - -} -*/ - -/* XXX: NYI -cairo_status_t -cairo_surface_clip_restore (cairo_surface_t *surface); -*/ - -cairo_status_t -cairo_surface_set_repeat (cairo_surface_t *surface, int repeat) -{ - if (surface == NULL) - return CAIRO_STATUS_NULL_POINTER; - - surface->repeat = repeat; - - return CAIRO_STATUS_SUCCESS; -} -slim_hidden_def(cairo_surface_set_repeat); - -typedef struct { - cairo_surface_t *dst; - cairo_rectangle_t extents; - cairo_image_surface_t *image; - cairo_rectangle_t image_rect; - void *image_extra; -} fallback_state_t; - -static cairo_status_t -_fallback_init (fallback_state_t *state, - cairo_surface_t *dst, - int x, - int y, - int width, - int height) -{ - state->extents.x = x; - state->extents.y = y; - state->extents.width = width; - state->extents.height = height; - - state->dst = dst; - - return _cairo_surface_acquire_dest_image (dst, &state->extents, - &state->image, &state->image_rect, &state->image_extra); -} - -static void -_fallback_cleanup (fallback_state_t *state) -{ - _cairo_surface_release_dest_image (state->dst, &state->extents, - state->image, &state->image_rect, state->image_extra); -} - -static cairo_status_t -_fallback_composite (cairo_operator_t operator, - cairo_pattern_t *src, - cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - fallback_state_t state; - cairo_status_t status; - - status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (!CAIRO_OK (status) || !state.image) - return status; - - state.image->base.backend->composite (operator, src, mask, - &state.image->base, - src_x, src_y, mask_x, mask_y, - dst_x - state.image_rect.x, - dst_y - state.image_rect.y, - width, height); - - _fallback_cleanup (&state); - - return status; -} - -cairo_status_t -_cairo_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src, - cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_int_status_t status; - - status = dst->backend->composite (operator, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - return _fallback_composite (operator, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); -} - -cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t operator, - cairo_color_t *color, - int x, - int y, - int width, - int height) -{ - cairo_rectangle_t rect; - - rect.x = x; - rect.y = y; - rect.width = width; - rect.height = height; - - return _cairo_surface_fill_rectangles (surface, operator, color, &rect, 1); -} - -static cairo_status_t -_fallback_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - fallback_state_t state; - cairo_rectangle_t *offset_rects = NULL; - cairo_status_t status; - int x1, y1, x2, y2; - int i; - - if (num_rects <= 0) - return CAIRO_STATUS_SUCCESS; - - /* Compute the bounds of the rectangles, so that we know what area of the - * destination surface to fetch - */ - x1 = rects[0].x; - y1 = rects[0].y; - x2 = rects[0].x + rects[0].width; - y2 = rects[0].y + rects[0].height; - - for (i = 1; i < num_rects; i++) { - if (rects[0].x < x1) - x1 = rects[0].x; - if (rects[0].y < y1) - y1 = rects[0].y; - - if (rects[0].x + rects[0].width > x2) - x2 = rects[0].x + rects[0].width; - if (rects[0].y + rects[0].height > y2) - y2 = rects[0].y + rects[0].height; - } - - status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); - if (!CAIRO_OK (status) || !state.image) - return status; - - /* If the fetched image isn't at 0,0, we need to offset the rectangles */ - - if (state.image_rect.x != 0 || state.image_rect.y != 0) { - offset_rects = malloc (sizeof (cairo_rectangle_t) * num_rects); - if (!offset_rects) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - for (i = 0; i < num_rects; i++) { - offset_rects[i].x = rects[i].x - state.image_rect.x; - offset_rects[i].y = rects[i].y - state.image_rect.y; - offset_rects[i].width = rects[i].width; - offset_rects[i].height = rects[i].height; - } - - rects = offset_rects; - } - - state.image->base.backend->fill_rectangles (&state.image->base, operator, color, - rects, num_rects); - - if (offset_rects) - free (offset_rects); - - FAIL: - _fallback_cleanup (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_int_status_t status; - - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; - - status = surface->backend->fill_rectangles (surface, - operator, - color, - rects, num_rects); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); -} - -static cairo_status_t -_fallback_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - fallback_state_t state; - cairo_trapezoid_t *offset_traps = NULL; - cairo_status_t status; - int i; - - status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (!CAIRO_OK (status) || !state.image) - return status; - - /* If the destination image isn't at 0,0, we need to offset the trapezoids */ - - if (state.image_rect.x != 0 || state.image_rect.y != 0) { - - cairo_fixed_t xoff = _cairo_fixed_from_int (state.image_rect.x); - cairo_fixed_t yoff = _cairo_fixed_from_int (state.image_rect.y); - - offset_traps = malloc (sizeof (cairo_trapezoid_t) * num_traps); - if (!offset_traps) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - - for (i = 0; i < num_traps; i++) { - offset_traps[i].top = traps[i].top - yoff; - offset_traps[i].bottom = traps[i].bottom - yoff; - offset_traps[i].left.p1.x = traps[i].left.p1.x - xoff; - offset_traps[i].left.p1.y = traps[i].left.p1.y - yoff; - offset_traps[i].left.p2.x = traps[i].left.p2.x - xoff; - offset_traps[i].left.p2.y = traps[i].left.p2.y - yoff; - offset_traps[i].right.p1.x = traps[i].right.p1.x - xoff; - offset_traps[i].right.p1.y = traps[i].right.p1.y - yoff; - offset_traps[i].right.p2.x = traps[i].right.p2.x - xoff; - offset_traps[i].right.p2.y = traps[i].right.p2.y - yoff; - } - - traps = offset_traps; - } - - state.image->base.backend->composite_trapezoids (operator, pattern, - &state.image->base, - src_x, src_y, - dst_x - state.image_rect.x, - dst_y - state.image_rect.y, - width, height, traps, num_traps); - if (offset_traps) - free (offset_traps); - - FAIL: - _fallback_cleanup (&state); - - return status; -} - - -cairo_status_t -_cairo_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - cairo_int_status_t status; - - status = dst->backend->composite_trapezoids (operator, - pattern, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - return _fallback_composite_trapezoids (operator, pattern, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps); -} - -cairo_status_t -_cairo_surface_copy_page (cairo_surface_t *surface) -{ - cairo_int_status_t status; - - status = surface->backend->copy_page (surface); - /* It's fine if some backends just don't support this. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return CAIRO_STATUS_SUCCESS; - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_show_page (cairo_surface_t *surface) -{ - cairo_int_status_t status; - - status = surface->backend->show_page (surface); - /* It's fine if some backends just don't support this. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return CAIRO_STATUS_SUCCESS; - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region) -{ - return surface->backend->set_clip_region (surface, region); -} diff --git a/src/cairo_traps.c b/src/cairo_traps.c deleted file mode 100644 index 79c7e16b6..000000000 --- a/src/cairo_traps.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - * Copyright © 2002 Keith Packard - * - * 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. - * - * The Initial Developer of the Original Code is Keith Packard - * - * Contributor(s): - * Keith R. Packard <keithp@keithp.com> - * Carl D. Worth <cworth@cworth.org> - * - * 2002-07-15: Converted from XRenderCompositeDoublePoly to cairo_trap. Carl D. Worth - */ - -#include "cairoint.h" - -/* private functions */ - -static cairo_status_t -_cairo_traps_grow_by (cairo_traps_t *traps, int additional); - -static cairo_status_t -_cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, - cairo_line_t *left, cairo_line_t *right); - -static cairo_status_t -_cairo_traps_add_trap_from_points (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, - cairo_point_t left_p1, cairo_point_t left_p2, - cairo_point_t right_p1, cairo_point_t right_p2); - -static int -_compare_point_fixed_by_y (const void *av, const void *bv); - -static int -_compare_cairo_edge_by_top (const void *av, const void *bv); - -static int -_compare_cairo_edge_by_slope (const void *av, const void *bv); - -static cairo_fixed_16_16_t -_compute_x (cairo_line_t *line, cairo_fixed_t y); - -static int -_line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret); - -void -_cairo_traps_init (cairo_traps_t *traps) -{ - traps->num_traps = 0; - - traps->traps_size = 0; - traps->traps = NULL; - traps->extents.p1.x = traps->extents.p1.y = CAIRO_MAXSHORT << 16; - traps->extents.p2.x = traps->extents.p2.y = CAIRO_MINSHORT << 16; -} - -void -_cairo_traps_fini (cairo_traps_t *traps) -{ - if (traps->traps_size) { - free (traps->traps); - traps->traps = NULL; - traps->traps_size = 0; - traps->num_traps = 0; - } -} - -static cairo_status_t -_cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, - cairo_line_t *left, cairo_line_t *right) -{ - cairo_status_t status; - cairo_trapezoid_t *trap; - - if (top == bottom) { - return CAIRO_STATUS_SUCCESS; - } - - if (traps->num_traps >= traps->traps_size) { - int inc = traps->traps_size ? traps->traps_size : 32; - status = _cairo_traps_grow_by (traps, inc); - if (status) - return status; - } - - trap = &traps->traps[traps->num_traps]; - trap->top = top; - trap->bottom = bottom; - trap->left = *left; - trap->right = *right; - - if (top < traps->extents.p1.y) - traps->extents.p1.y = top; - if (bottom > traps->extents.p2.y) - traps->extents.p2.y = bottom; - /* - * This isn't generally accurate, but it is close enough for - * this purpose. Assuming that the left and right segments always - * contain the trapezoid vertical extents, these compares will - * yield a containing box. Assuming that the points all come from - * the same figure which will eventually be completely drawn, then - * the compares will yield the correct overall extents - */ - if (left->p1.x < traps->extents.p1.x) - traps->extents.p1.x = left->p1.x; - if (left->p2.x < traps->extents.p1.x) - traps->extents.p1.x = left->p2.x; - - if (right->p1.x > traps->extents.p2.x) - traps->extents.p2.x = right->p1.x; - if (right->p2.x > traps->extents.p2.x) - traps->extents.p2.x = right->p2.x; - - traps->num_traps++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_traps_add_trap_from_points (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, - cairo_point_t left_p1, cairo_point_t left_p2, - cairo_point_t right_p1, cairo_point_t right_p2) -{ - cairo_line_t left; - cairo_line_t right; - - left.p1 = left_p1; - left.p2 = left_p2; - - right.p1 = right_p1; - right.p2 = right_p2; - - return _cairo_traps_add_trap (traps, top, bottom, &left, &right); -} - -static cairo_status_t -_cairo_traps_grow_by (cairo_traps_t *traps, int additional) -{ - cairo_trapezoid_t *new_traps; - int old_size = traps->traps_size; - int new_size = traps->num_traps + additional; - - if (new_size <= traps->traps_size) { - return CAIRO_STATUS_SUCCESS; - } - - traps->traps_size = new_size; - new_traps = realloc (traps->traps, traps->traps_size * sizeof (cairo_trapezoid_t)); - - if (new_traps == NULL) { - traps->traps_size = old_size; - return CAIRO_STATUS_NO_MEMORY; - } - - traps->traps = new_traps; - - return CAIRO_STATUS_SUCCESS; -} - -static int -_compare_point_fixed_by_y (const void *av, const void *bv) -{ - const cairo_point_t *a = av, *b = bv; - - int ret = a->y - b->y; - if (ret == 0) { - ret = a->x - b->x; - } - return ret; -} - -cairo_status_t -_cairo_traps_tessellate_triangle (cairo_traps_t *traps, cairo_point_t t[3]) -{ - cairo_status_t status; - cairo_line_t line; - cairo_fixed_16_16_t intersect; - cairo_point_t tsort[3]; - - memcpy (tsort, t, 3 * sizeof (cairo_point_t)); - qsort (tsort, 3, sizeof (cairo_point_t), _compare_point_fixed_by_y); - - /* horizontal top edge requires special handling */ - if (tsort[0].y == tsort[1].y) { - if (tsort[0].x < tsort[1].x) - status = _cairo_traps_add_trap_from_points (traps, - tsort[1].y, tsort[2].y, - tsort[0], tsort[2], - tsort[1], tsort[2]); - else - status = _cairo_traps_add_trap_from_points (traps, - tsort[1].y, tsort[2].y, - tsort[1], tsort[2], - tsort[0], tsort[2]); - return status; - } - - line.p1 = tsort[0]; - line.p2 = tsort[1]; - - intersect = _compute_x (&line, tsort[2].y); - - if (intersect < tsort[2].x) { - status = _cairo_traps_add_trap_from_points (traps, - tsort[0].y, tsort[1].y, - tsort[0], tsort[1], - tsort[0], tsort[2]); - if (status) - return status; - status = _cairo_traps_add_trap_from_points (traps, - tsort[1].y, tsort[2].y, - tsort[1], tsort[2], - tsort[0], tsort[2]); - if (status) - return status; - } else { - status = _cairo_traps_add_trap_from_points (traps, - tsort[0].y, tsort[1].y, - tsort[0], tsort[2], - tsort[0], tsort[1]); - if (status) - return status; - status = _cairo_traps_add_trap_from_points (traps, - tsort[1].y, tsort[2].y, - tsort[0], tsort[2], - tsort[1], tsort[2]); - if (status) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -/* Warning: This function reorders the elements of the array provided. */ -cairo_status_t -_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, cairo_point_t q[4]) -{ - cairo_status_t status; - - qsort (q, 4, sizeof (cairo_point_t), _compare_point_fixed_by_y); - - if (q[1].x > q[2].x) { - status = _cairo_traps_add_trap_from_points (traps, - q[0].y, q[1].y, q[0], q[2], q[0], q[1]); - if (status) - return status; - status = _cairo_traps_add_trap_from_points (traps, - q[1].y, q[2].y, q[0], q[2], q[1], q[3]); - if (status) - return status; - status = _cairo_traps_add_trap_from_points (traps, - q[2].y, q[3].y, q[2], q[3], q[1], q[3]); - if (status) - return status; - } else { - status = _cairo_traps_add_trap_from_points (traps, - q[0].y, q[1].y, q[0], q[1], q[0], q[2]); - if (status) - return status; - status = _cairo_traps_add_trap_from_points (traps, - q[1].y, q[2].y, q[1], q[3], q[0], q[2]); - if (status) - return status; - status = _cairo_traps_add_trap_from_points (traps, - q[2].y, q[3].y, q[1], q[3], q[2], q[3]); - if (status) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static int -_compare_cairo_edge_by_top (const void *av, const void *bv) -{ - const cairo_edge_t *a = av, *b = bv; - - return a->edge.p1.y - b->edge.p1.y; -} - -/* Return value is: - > 0 if a is "clockwise" from b, (in a mathematical, not a graphical sense) - == 0 if slope (a) == slope (b) - < 0 if a is "counter-clockwise" from b -*/ -static int -_compare_cairo_edge_by_slope (const void *av, const void *bv) -{ - const cairo_edge_t *a = av, *b = bv; - cairo_fixed_32_32_t d; - - cairo_fixed_48_16_t a_dx = a->edge.p2.x - a->edge.p1.x; - cairo_fixed_48_16_t a_dy = a->edge.p2.y - a->edge.p1.y; - cairo_fixed_48_16_t b_dx = b->edge.p2.x - b->edge.p1.x; - cairo_fixed_48_16_t b_dy = b->edge.p2.y - b->edge.p1.y; - - d = b_dy * a_dx - a_dy * b_dx; - - if (d > 0) - return 1; - else if (d == 0) - return 0; - else - return -1; -} - -static int -_compare_cairo_edge_by_current_x_slope (const void *av, const void *bv) -{ - const cairo_edge_t *a = av, *b = bv; - int ret; - - ret = a->current_x - b->current_x; - if (ret == 0) - ret = _compare_cairo_edge_by_slope (a, b); - return ret; -} - -/* XXX: Both _compute_x and _compute_inverse_slope will divide by zero - for horizontal lines. Now, we "know" that when we are tessellating - polygons that the polygon data structure discards all horizontal - edges, but there's nothing here to guarantee that. I suggest the - following: - - A) Move all of the polygon tessellation code out of xrtraps.c and - into xrpoly.c, (in order to be in the same module as the code - discarding horizontal lines). - - OR - - B) Re-implement the line intersection in a way that avoids all - division by zero. Here's one approach. The only disadvantage - might be that that there are not meaningful names for all of the - sub-computations -- just a bunch of determinants. I haven't - looked at complexity, (both are probably similar and it probably - doesn't matter much anyway). - */ - -/* XXX: Keith's new intersection code is much cleaner, and uses - * sufficient precision for correctly sorting intersections according - * to the analysis in Hobby's paper. - * - * But, when we enable this code, some things are failing, (eg. the - * stars in test/fill_rule get filled wrong). This could indicate a - * bug in one of tree places: - * - * 1) The new intersection code in this file - * - * 2) cairo_wideint.c (which is only exercised here) - * - * 3) In the current tessellator, (where the old intersection - * code, with its mystic increments could be masking the bug). - * - * It will likely be easier to revisit this when the new tessellation - * code is in place. So, for now, we'll simply disable the new - * intersection code. - */ - -#define CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE 0 - -#if CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE -static const cairo_fixed_32_32_t -_det16_32 (cairo_fixed_16_16_t a, - cairo_fixed_16_16_t b, - cairo_fixed_16_16_t c, - cairo_fixed_16_16_t d) -{ - return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), - _cairo_int32x32_64_mul (b, c)); -} - -static const cairo_fixed_64_64_t -_det32_64 (cairo_fixed_32_32_t a, - cairo_fixed_32_32_t b, - cairo_fixed_32_32_t c, - cairo_fixed_32_32_t d) -{ - return _cairo_int128_sub (_cairo_int64x64_128_mul (a, d), - _cairo_int64x64_128_mul (b, c)); -} - -static const cairo_fixed_32_32_t -_fixed_16_16_to_fixed_32_32 (cairo_fixed_16_16_t a) -{ - return _cairo_int64_lsl (_cairo_int32_to_int64 (a), 16); -} - -static int -_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_intersection) -{ - cairo_fixed_16_16_t dx1, dx2, dy1, dy2; - cairo_fixed_32_32_t den_det; - cairo_fixed_32_32_t l1_det, l2_det; - cairo_fixed_64_64_t num_det; - cairo_fixed_32_32_t intersect_32_32; - cairo_fixed_48_16_t intersect_48_16; - cairo_fixed_16_16_t intersect_16_16; - cairo_quorem128_t qr; - - dx1 = l1->p1.x - l1->p2.x; - dy1 = l1->p1.y - l1->p2.y; - dx2 = l2->p1.x - l2->p2.x; - dy2 = l2->p1.y - l2->p2.y; - den_det = _det16_32 (dx1, dy1, - dx2, dy2); - - if (_cairo_int64_eq (den_det, _cairo_int32_to_int64(0))) - return 0; - - l1_det = _det16_32 (l1->p1.x, l1->p1.y, - l1->p2.x, l1->p2.y); - l2_det = _det16_32 (l2->p1.x, l2->p1.y, - l2->p2.x, l2->p2.y); - - - num_det = _det32_64 (l1_det, _fixed_16_16_to_fixed_32_32 (dy1), - l2_det, _fixed_16_16_to_fixed_32_32 (dy2)); - - /* - * Ok, this one is a bit tricky in fixed point, the denominator - * needs to be left with 32-bits of fraction so that the - * result of the divide ends up with 32-bits of fraction (64 - 32 = 32) - */ - qr = _cairo_int128_divrem (num_det, _cairo_int64_to_int128 (den_det)); - - intersect_32_32 = _cairo_int128_to_int64 (qr.quo); - - /* - * Find the ceiling of the quotient -- divrem returns - * the quotient truncated towards zero, so if the - * quotient should be positive (num_den and den_det have same sign) - * bump the quotient up by one. - */ - - if (_cairo_int128_ne (qr.rem, _cairo_int32_to_int128 (0)) && - (_cairo_int128_ge (num_det, _cairo_int32_to_int128 (0)) == - _cairo_int64_ge (den_det, _cairo_int32_to_int64 (0)))) - { - intersect_32_32 = _cairo_int64_add (intersect_32_32, - _cairo_int32_to_int64 (1)); - } - - /* - * Now convert from 32.32 to 48.16 and take the ceiling; - * this requires adding in 15 1 bits and shifting the result - */ - - intersect_32_32 = _cairo_int64_add (intersect_32_32, - _cairo_int32_to_int64 ((1 << 16) - 1)); - intersect_48_16 = _cairo_int64_rsa (intersect_32_32, 16); - - /* - * And drop the top bits - */ - intersect_16_16 = _cairo_int64_to_int32 (intersect_48_16); - - *y_intersection = intersect_16_16; - - return 1; -} -#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ - -static cairo_fixed_16_16_t -_compute_x (cairo_line_t *line, cairo_fixed_t y) -{ - cairo_fixed_16_16_t dx = line->p2.x - line->p1.x; - cairo_fixed_32_32_t ex = (cairo_fixed_48_16_t) (y - line->p1.y) * (cairo_fixed_48_16_t) dx; - cairo_fixed_16_16_t dy = line->p2.y - line->p1.y; - - return line->p1.x + (ex / dy); -} - -#if ! CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE -static double -_compute_inverse_slope (cairo_line_t *l) -{ - return (_cairo_fixed_to_double (l->p2.x - l->p1.x) / - _cairo_fixed_to_double (l->p2.y - l->p1.y)); -} - -static double -_compute_x_intercept (cairo_line_t *l, double inverse_slope) -{ - return _cairo_fixed_to_double (l->p1.x) - inverse_slope * _cairo_fixed_to_double (l->p1.y); -} - -static int -_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ret) -{ - /* - * x = m1y + b1 - * x = m2y + b2 - * m1y + b1 = m2y + b2 - * y * (m1 - m2) = b2 - b1 - * y = (b2 - b1) / (m1 - m2) - */ - cairo_fixed_16_16_t y_intersect; - double m1 = _compute_inverse_slope (l1); - double b1 = _compute_x_intercept (l1, m1); - double m2 = _compute_inverse_slope (l2); - double b2 = _compute_x_intercept (l2, m2); - - if (m1 == m2) - return 0; - - y_intersect = _cairo_fixed_from_double ((b2 - b1) / (m1 - m2)); - - if (m1 < m2) { - cairo_line_t *t; - t = l1; - l1 = l2; - l2 = t; - } - - /* Assuming 56 bits of floating point precision, the intersection - is accurate within one sub-pixel coordinate. We must ensure - that we return a value that is at or after the intersection. At - most, we must increment once. */ - if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect)) - y_intersect++; - /* XXX: Hmm... Keith's error calculations said we'd at most be off - by one sub-pixel. But, I found that the paint-fill-BE-01.svg - test from the W3C SVG conformance suite definitely requires two - increments. - - It could be that we need one to overcome the error, and another - to round up. - - It would be nice to be sure this code is correct, (but we can't - do the while loop as it will work for way to long on - exceedingly distant intersections with large errors that we - really don't care about anyway as they will be ignored by the - calling function. - */ - if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect)) - y_intersect++; - /* XXX: hmm... now I found "intersection_killer" inside xrspline.c - that requires 3 increments. Clearly, we haven't characterized - this completely yet. */ - if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect)) - y_intersect++; - /* I think I've found the answer to our problems. The insight is - that everytime we round we are changing the slopes of the - relevant lines, so we may be introducing new intersections that - we miss, so everything breaks apart. John Hobby wrote a paper - on how to fix this: - - [Hobby93c] John D. Hobby, Practical Segment Intersection with - Finite Precision Output, Computation Geometry Theory and - Applications, 13(4), 1999. - - Available online (2003-08017): - - http://cm.bell-labs.com/cm/cs/doc/93/2-27.ps.gz - - Now we just need to go off and implement that. - */ - - *y_ret = y_intersect; - - return 1; -} -#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ - -/* The algorithm here is pretty simple: - - inactive = [edges] - y = min_p1_y (inactive) - - while (num_active || num_inactive) { - active = all edges containing y - - next_y = min ( min_p2_y (active), min_p1_y (inactive), min_intersection (active) ) - - fill_traps (active, y, next_y, fill_rule) - - y = next_y - } - - The invariants that hold during fill_traps are: - - All edges in active contain both y and next_y - No edges in active intersect within y and next_y - - These invariants mean that fill_traps is as simple as sorting the - active edges, forming a trapezoid between each adjacent pair. Then, - either the even-odd or winding rule is used to determine whether to - emit each of these trapezoids. - - Warning: This function obliterates the edges of the polygon provided. -*/ -cairo_status_t -_cairo_traps_tessellate_polygon (cairo_traps_t *traps, - cairo_polygon_t *poly, - cairo_fill_rule_t fill_rule) -{ - cairo_status_t status; - int i, active, inactive; - cairo_fixed_t y, y_next, intersect; - int in_out, num_edges = poly->num_edges; - cairo_edge_t *edges = poly->edges; - - if (num_edges == 0) - return CAIRO_STATUS_SUCCESS; - - qsort (edges, num_edges, sizeof (cairo_edge_t), _compare_cairo_edge_by_top); - - y = edges[0].edge.p1.y; - active = 0; - inactive = 0; - while (active < num_edges) { - while (inactive < num_edges && edges[inactive].edge.p1.y <= y) - inactive++; - - for (i = active; i < inactive; i++) - edges[i].current_x = _compute_x (&edges[i].edge, y); - - qsort (&edges[active], inactive - active, - sizeof (cairo_edge_t), _compare_cairo_edge_by_current_x_slope); - - /* find next inflection point */ - y_next = edges[active].edge.p2.y; - - for (i = active; i < inactive; i++) { - if (edges[i].edge.p2.y < y_next) - y_next = edges[i].edge.p2.y; - /* check intersect */ - if (i != inactive - 1 && edges[i].current_x != edges[i+1].current_x) - if (_line_segs_intersect_ceil (&edges[i].edge, &edges[i+1].edge, - &intersect)) - if (intersect > y && intersect < y_next) - y_next = intersect; - } - /* check next inactive point */ - if (inactive < num_edges && edges[inactive].edge.p1.y < y_next) - y_next = edges[inactive].edge.p1.y; - - /* walk the active edges generating trapezoids */ - in_out = 0; - for (i = active; i < inactive - 1; i++) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) { - if (edges[i].clockWise) - in_out++; - else - in_out--; - if (in_out == 0) - continue; - } else { - in_out++; - if ((in_out & 1) == 0) - continue; - } - status = _cairo_traps_add_trap (traps, y, y_next, &edges[i].edge, &edges[i+1].edge); - if (status) - return status; - } - - /* delete inactive edges */ - for (i = active; i < inactive; i++) { - if (edges[i].edge.p2.y <= y_next) { - memmove (&edges[active+1], &edges[active], (i - active) * sizeof (cairo_edge_t)); - active++; - } - } - - y = y_next; - } - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) -{ - cairo_slope_t slope_left, slope_pt, slope_right; - - if (t->top > pt->y) - return FALSE; - if (t->bottom < pt->y) - return FALSE; - - _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); - _cairo_slope_init (&slope_pt, &t->left.p1, pt); - - if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) - return FALSE; - - _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); - _cairo_slope_init (&slope_pt, &t->right.p1, pt); - - if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) - return FALSE; - - return TRUE; -} - -cairo_bool_t -_cairo_traps_contain (cairo_traps_t *traps, double x, double y) -{ - int i; - cairo_point_t point; - - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); - - for (i = 0; i < traps->num_traps; i++) { - if (_cairo_trap_contains (&traps->traps[i], &point)) - return TRUE; - } - - return FALSE; -} - -void -_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents) -{ - *extents = traps->extents; -} diff --git a/src/cairo_unicode.c b/src/cairo_unicode.c deleted file mode 100644 index 92201391a..000000000 --- a/src/cairo_unicode.c +++ /dev/null @@ -1,340 +0,0 @@ -/* cairo_unicode.c: Unicode conversion routines - * - * The code in this file is derived from GLib's gutf8.c and - * ultimately from libunicode. It is relicensed under the - * dual LGPL/MPL with permission of the original authors. - * - * Copyright © 1999 Tom Tromey - * Copyright © 2005 Red Hat, Inc - * - * 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 cairo_unicode.c as distributed with the - * cairo graphics library. - * - * The Initial Developer of the Original Code is Tom Tromey. - * and Red Hat, Inc. - * - * Contributor(s): - * Owen Taylor <otaylor@redhat.com> - */ - -#include <limits.h> - -#include <cairoint.h> - -#define UTF8_COMPUTE(Char, Mask, Len) \ - if (Char < 128) \ - { \ - Len = 1; \ - Mask = 0x7f; \ - } \ - else if ((Char & 0xe0) == 0xc0) \ - { \ - Len = 2; \ - Mask = 0x1f; \ - } \ - else if ((Char & 0xf0) == 0xe0) \ - { \ - Len = 3; \ - Mask = 0x0f; \ - } \ - else if ((Char & 0xf8) == 0xf0) \ - { \ - Len = 4; \ - Mask = 0x07; \ - } \ - else if ((Char & 0xfc) == 0xf8) \ - { \ - Len = 5; \ - Mask = 0x03; \ - } \ - else if ((Char & 0xfe) == 0xfc) \ - { \ - Len = 6; \ - Mask = 0x01; \ - } \ - else \ - Len = -1; - -#define UTF8_LENGTH(Char) \ - ((Char) < 0x80 ? 1 : \ - ((Char) < 0x800 ? 2 : \ - ((Char) < 0x10000 ? 3 : \ - ((Char) < 0x200000 ? 4 : \ - ((Char) < 0x4000000 ? 5 : 6))))) - - -#define UTF8_GET(Result, Chars, Count, Mask, Len) \ - (Result) = (Chars)[0] & (Mask); \ - for ((Count) = 1; (Count) < (Len); ++(Count)) \ - { \ - if (((Chars)[(Count)] & 0xc0) != 0x80) \ - { \ - (Result) = -1; \ - break; \ - } \ - (Result) <<= 6; \ - (Result) |= ((Chars)[(Count)] & 0x3f); \ - } - -#define UNICODE_VALID(Char) \ - ((Char) < 0x110000 && \ - (((Char) & 0xFFFFF800) != 0xD800) && \ - ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ - ((Char) & 0xFFFE) != 0xFFFE) - - -static const char utf8_skip_data[256] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 -}; - -#define UTF8_NEXT_CHAR(p) (char *)((p) + utf8_skip_data[*(unsigned char *)(p)]) - -/* Converts a sequence of bytes encoded as UTF-8 to a Unicode character. - * If @p does not point to a valid UTF-8 encoded character, results are - * undefined. - **/ -static uint32_t -_utf8_get_char (const char *p) -{ - int i, mask = 0, len; - uint32_t result; - unsigned char c = (unsigned char) *p; - - UTF8_COMPUTE (c, mask, len); - if (len == -1) - return (uint32_t)-1; - UTF8_GET (result, p, i, mask, len); - - return result; -} - -/* Like _utf8_get_char, but take a maximum length - * and return (uint32_t)-2 on incomplete trailing character - */ -static uint32_t -_utf8_get_char_extended (const char *p, - long max_len) -{ - int i, len; - uint32_t wc = (unsigned char) *p; - - if (wc < 0x80) { - return wc; - } else if (wc < 0xc0) { - return (uint32_t)-1; - } else if (wc < 0xe0) { - len = 2; - wc &= 0x1f; - } else if (wc < 0xf0) { - len = 3; - wc &= 0x0f; - } else if (wc < 0xf8) { - len = 4; - wc &= 0x07; - } else if (wc < 0xfc) { - len = 5; - wc &= 0x03; - } else if (wc < 0xfe) { - len = 6; - wc &= 0x01; - } else { - return (uint32_t)-1; - } - - if (max_len >= 0 && len > max_len) { - for (i = 1; i < max_len; i++) { - if ((((unsigned char *)p)[i] & 0xc0) != 0x80) - return (uint32_t)-1; - } - return (uint32_t)-2; - } - - for (i = 1; i < len; ++i) { - uint32_t ch = ((unsigned char *)p)[i]; - - if ((ch & 0xc0) != 0x80) { - if (ch) - return (uint32_t)-1; - else - return (uint32_t)-2; - } - - wc <<= 6; - wc |= (ch & 0x3f); - } - - if (UTF8_LENGTH(wc) != len) - return (uint32_t)-1; - - return wc; -} - -/** - * _cairo_utf8_to_utf32: - * @str: an UTF-8 string - * @len: length of @str in bytes, or -1 if it is nul-terminated. - * If @len is supplied and the string has an embedded nul - * byte, only the portion before the nul byte is converted. - * @result: location to store a pointer to a newly allocated UTF-32 - * string (always native endian). Free with free(). A 0 - * word will be written after the last character. - * @items_written: location to store number of 32-bit words - * written. (Not including the trailing 0) - * - * Converts a UTF-8 string to UCS-4. UCS-4 is an encoding of Unicode - * with 1 32-bit word per character. The string is validated to - * consist entirely of valid Unicode characters. - * - * Return value: %CAIRO_STATUS_SUCCESS if the entire string was - * succesfully converted. %CAIRO_STATUS_INVALID_STRING if an - * an invalid sequence was found. - **/ -cairo_status_t -_cairo_utf8_to_ucs4 (const char *str, - int len, - uint32_t **result, - int *items_written) -{ - uint32_t *str32 = NULL; - int n_chars, i; - const char *in; - - in = str; - n_chars = 0; - while ((len < 0 || str + len - in > 0) && *in) - { - uint32_t wc = _utf8_get_char_extended (in, str + len - in); - if (wc & 0x80000000 || !UNICODE_VALID (wc)) - return CAIRO_STATUS_INVALID_STRING; - - n_chars++; - if (n_chars == INT_MAX) - return CAIRO_STATUS_INVALID_STRING; - - in = UTF8_NEXT_CHAR (in); - } - - str32 = malloc (sizeof (uint32_t) * (n_chars + 1)); - if (!str32) - return CAIRO_STATUS_NO_MEMORY; - - in = str; - for (i=0; i < n_chars; i++) { - str32[i] = _utf8_get_char (in); - in = UTF8_NEXT_CHAR (in); - } - str32[i] = 0; - - *result = str32; - if (items_written) - *items_written = n_chars; - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_utf8_to_utf16: - * @str: an UTF-8 string - * @len: length of @str in bytes, or -1 if it is nul-terminated. - * If @len is supplied and the string has an embedded nul - * byte, only the portion before the nul byte is converted. - * @result: location to store a pointer to a newly allocated UTF-16 - * string (always native endian). Free with free(). A 0 - * word will be written after the last character. - * @items_written: location to store number of 16-bit words - * written. (Not including the trailing 0) - * - * Converts a UTF-8 string to UTF-16. UTF-16 is an encoding of Unicode - * where characters are represented either as a single 16-bit word, or - * as a pair of 16-bit "surrogates". The string is validated to - * consist entirely of valid Unicode characters. - * - * Return value: %CAIRO_STATUS_SUCCESS if the entire string was - * succesfully converted. %CAIRO_STATUS_INVALID_STRING if an - * an invalid sequence was found. - **/ -cairo_status_t -_cairo_utf8_to_utf16 (const char *str, - int len, - uint16_t **result, - int *items_written) -{ - uint16_t *str16 = NULL; - int n16, i; - const char *in; - - in = str; - n16 = 0; - while ((len < 0 || str + len - in > 0) && *in) { - uint32_t wc = _utf8_get_char_extended (in, str + len - in); - if (wc & 0x80000000 || !UNICODE_VALID (wc)) - return CAIRO_STATUS_INVALID_STRING; - - if (wc < 0x10000) - n16 += 1; - else - n16 += 2; - - if (n16 == INT_MAX - 1 || n16 == INT_MAX) - return CAIRO_STATUS_INVALID_STRING; - - in = UTF8_NEXT_CHAR (in); - } - - - str16 = malloc (sizeof (uint16_t) * (n16 + 1)); - if (!str16) - return CAIRO_STATUS_NO_MEMORY; - - in = str; - for (i = 0; i < n16;) { - uint32_t wc = _utf8_get_char (in); - - if (wc < 0x10000) { - str16[i++] = wc; - } else { - str16[i++] = (wc - 0x10000) / 0x400 + 0xd800; - str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00; - } - - in = UTF8_NEXT_CHAR (in); - } - - str16[i] = 0; - - *result = str16; - if (items_written) - *items_written = n16; - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo_wideint.c b/src/cairo_wideint.c deleted file mode 100644 index b636dface..000000000 --- a/src/cairo_wideint.c +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * $Id: cairo_wideint.c,v 1.4 2005-01-19 15:07:00 cworth Exp $ - * - * Copyright © 2004 Keith Packard - * - * 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. - * - * The Initial Developer of the Original Code is Keith Packard - * - * Contributor(s): - * Keith R. Packard <keithp@keithp.com> - */ - -#include "cairoint.h" - -#if !HAVE_UINT64_T || !HAVE_UINT128_T - -static const unsigned char top_bit[256] = -{ - 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, -}; - -#endif - -#if HAVE_UINT64_T - -#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l)) - -cairo_uquorem64_t -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) -{ - cairo_uquorem64_t qr; - - qr.quo = num / den; - qr.rem = num % den; - return qr; -} - -#else - -cairo_uint64_t -_cairo_uint32_to_uint64 (uint32_t i) -{ - cairo_uint64_t q; - - q.lo = i; - q.hi = 0; - return q; -} - -cairo_int64_t -_cairo_int32_to_int64 (int32_t i) -{ - cairo_uint64_t q; - - q.lo = i; - q.hi = i < 0 ? -1 : 0; - return q; -} - -static const cairo_uint64_t -_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l) -{ - cairo_uint64_t q; - - q.lo = l; - q.hi = h; - return q; -} - -cairo_uint64_t -_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint64_t s; - - s.hi = a.hi + b.hi; - s.lo = a.lo + b.lo; - if (s.lo < a.lo) - s.hi++; - return s; -} - -cairo_uint64_t -_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint64_t s; - - s.hi = a.hi - b.hi; - s.lo = a.lo - b.lo; - if (s.lo > a.lo) - s.hi--; - return s; -} - -#define uint32_lo(i) ((i) & 0xffff) -#define uint32_hi(i) ((i) >> 16) -#define uint32_carry16 ((1) << 16) - -cairo_uint64_t -_cairo_uint32x32_64_mul (uint32_t a, uint32_t b) -{ - cairo_uint64_t s; - - uint16_t ah, al, bh, bl; - uint32_t r0, r1, r2, r3; - - al = uint32_lo (a); - ah = uint32_hi (a); - bl = uint32_lo (b); - bh = uint32_hi (b); - - r0 = (uint32_t) al * bl; - r1 = (uint32_t) al * bh; - r2 = (uint32_t) ah * bl; - r3 = (uint32_t) ah * bh; - - r1 += uint32_hi(r0); /* no carry possible */ - r1 += r2; /* but this can carry */ - if (r1 < r2) /* check */ - r3 += uint32_carry16; - - s.hi = r3 + uint32_hi(r1); - s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0); - return s; -} - -cairo_int64_t -_cairo_int32x32_64_mul (int32_t a, int32_t b) -{ - s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t b)); - if (a < 0) - s.hi -= b; - if (b < 0) - s.hi -= a; - return s; -} - -cairo_uint64_t -_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint64_t s; - - s = _cairo_uint32x32_64_mul (a.lo, b.lo); - s.hi += a.lo * b.hi + a.hi * b.lo; - return s; -} - -cairo_uint64_t -_cairo_uint64_lsl (cairo_uint64_t a, int shift) -{ - if (shift >= 32) - { - a.hi = a.lo; - a.lo = 0; - shift -= 32; - } - if (shift) - { - a.hi = a.hi << shift | a.lo >> (32 - shift); - a.lo = a.lo << shift; - } - return a; -} - -cairo_uint64_t -_cairo_uint64_rsl (cairo_uint64_t a, int shift) -{ - if (shift >= 32) - { - a.lo = a.hi; - a.hi = 0; - shift -= 32; - } - if (shift) - { - a.lo = a.lo >> shift | a.hi << (32 - shift); - a.hi = a.hi >> shift; - } - return a; -} - -#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n))) - -cairo_int64_t -_cairo_uint64_rsa (cairo_int64_t a, int shift) -{ - if (shift >= 32) - { - a.lo = a.hi; - a.hi = _cairo_uint32_rsa (a.hi, 31); - shift -= 32; - } - if (shift) - { - a.lo = a.lo >> shift | a.hi << (32 - shift); - a.hi = _cairo_uint32_rsa (a.hi, shift); - } - return a; -} - -int -_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b) -{ - return (a.hi < b.hi || - (a.hi == b.hi && a.lo < b.lo)); -} - -int -_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b) -{ - return a.hi == b.hi && a.lo == b.lo; -} - -int -_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b) -{ - if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) - return 1; - if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) - return 0; - return _cairo_uint64_lt (a, b); -} - -cairo_uint64_t -_cairo_uint64_not (cairo_uint64_t a) -{ - a.lo = ~a.lo; - a.hi = ~a.hi; - return a; -} - -cairo_uint64_t -_cairo_uint64_negate (cairo_uint64_t a) -{ - a.lo = ~a.lo; - a.hi = ~a.hi; - if (++a.lo == 0) - ++a.hi; - return a; -} - -/* - * The design of this algorithm comes from GCC, - * but the actual implementation is new - */ - -static const int -_cairo_leading_zeros32 (uint32_t i) -{ - int top; - - if (i < 0x100) - top = 0; - else if (i < 0x10000) - top = 8; - else if (i < 0x1000000) - top = 16; - else - top = 24; - top = top + top_bit [i >> top]; - return 32 - top; -} - -typedef struct _cairo_uquorem32_t { - uint32_t quo; - uint32_t rem; -} cairo_uquorem32_t; - -/* - * den >= num.hi - */ -static const cairo_uquorem32_t -_cairo_uint64x32_normalized_divrem (cairo_uint64_t num, uint32_t den) -{ - cairo_uquorem32_t qr; - uint32_t q0, q1, r0, r1; - uint16_t d0, d1; - uint32_t t; - - d0 = den & 0xffff; - d1 = den >> 16; - - q1 = num.hi / d1; - r1 = num.hi % d1; - - t = q1 * d0; - r1 = (r1 << 16) | (num.lo >> 16); - if (r1 < t) - { - q1--; - r1 += den; - if (r1 >= den && r1 < t) - { - q1--; - r1 += den; - } - } - - r1 -= t; - - q0 = r1 / d1; - r0 = r1 % d1; - t = q0 * d0; - r0 = (r0 << 16) | (num.lo & 0xffff); - if (r0 < t) - { - q0--; - r0 += den; - if (r0 >= den && r0 < t) - { - q0--; - r0 += den; - } - } - r0 -= t; - qr.quo = (q1 << 16) | q0; - qr.rem = r0; - return qr; -} - -cairo_uquorem64_t -_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) -{ - cairo_uquorem32_t qr32; - cairo_uquorem64_t qr; - int norm; - uint32_t q1, q0, r1, r0; - - if (den.hi == 0) - { - if (den.lo > num.hi) - { - /* 0q = nn / 0d */ - - norm = _cairo_leading_zeros32 (den.lo); - if (norm) - { - den.lo <<= norm; - num = _cairo_uint64_lsl (num, norm); - } - q1 = 0; - } - else - { - /* qq = NN / 0d */ - - if (den.lo == 0) - den.lo = 1 / den.lo; - - norm = _cairo_leading_zeros32 (den.lo); - if (norm) - { - cairo_uint64_t num1; - den.lo <<= norm; - num1 = _cairo_uint64_rsl (num, 32 - norm); - qr32 = _cairo_uint64x32_normalized_divrem (num1, den.lo); - q1 = qr32.quo; - num.hi = qr32.rem; - num.lo <<= norm; - } - else - { - num.hi -= den.lo; - q1 = 1; - } - } - qr32 = _cairo_uint64x32_normalized_divrem (num, den.lo); - q0 = qr32.quo; - r1 = 0; - r0 = qr32.rem >> norm; - } - else - { - if (den.hi > num.hi) - { - /* 00 = nn / DD */ - q0 = q1 = 0; - r0 = num.lo; - r1 = num.hi; - } - else - { - /* 0q = NN / dd */ - - norm = _cairo_leading_zeros32 (den.hi); - if (norm == 0) - { - if (num.hi > den.hi || num.lo >= den.lo) - { - q0 = 1; - num = _cairo_uint64_sub (num, den); - } - else - q0 = 0; - - q1 = 0; - r0 = num.lo; - r1 = num.hi; - } - else - { - cairo_uint64_t num1; - cairo_uint64_t part; - - num1 = _cairo_uint64_rsl (num, 32 - norm); - den = _cairo_uint64_lsl (den, norm); - - qr32 = _cairo_uint64x32_normalized_divrem (num1, den.hi); - part = _cairo_uint32x32_64_mul (qr32.quo, den.lo); - - q0 = qr32.quo; - - num.lo <<= norm; - num.hi = qr32.rem; - - if (_cairo_uint64_gt (part, num)) - { - q0--; - part = _cairo_uint64_sub (part, den); - } - - q1 = 0; - - num = _cairo_uint64_sub (num, part); - num = _cairo_uint64_rsl (num, norm); - r0 = num.lo; - r1 = num.hi; - } - } - } - qr.quo.lo = q0; - qr.quo.hi = q1; - qr.rem.lo = r0; - qr.rem.hi = r1; - return qr; -} - -#endif /* !HAVE_UINT64_T */ - -cairo_quorem64_t -_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) -{ - int num_neg = _cairo_int64_negative (num); - int den_neg = _cairo_int64_negative (den); - cairo_uquorem64_t uqr; - cairo_quorem64_t qr; - - if (num_neg) - num = _cairo_int64_negate (num); - if (den_neg) - den = _cairo_int64_negate (den); - uqr = _cairo_uint64_divrem (num, den); - if (num_neg) - qr.rem = _cairo_int64_negate (uqr.rem); - else - qr.rem = uqr.rem; - if (num_neg != den_neg) - qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); - else - qr.quo = (cairo_int64_t) uqr.quo; - return qr; -} - -#if HAVE_UINT128_T - -cairo_uquorem128_t -_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) -{ - cairo_uquorem128_t qr; - - qr.quo = num / den; - qr.rem = num % den; - return qr; -} - -#else - -cairo_uint128_t -_cairo_uint32_to_uint128 (uint32_t i) -{ - cairo_uint128_t q; - - q.lo = _cairo_uint32_to_uint64 (i); - q.hi = _cairo_uint32_to_uint64 (0); - return q; -} - -cairo_int128_t -_cairo_int32_to_int128 (int32_t i) -{ - cairo_int128_t q; - - q.lo = _cairo_int32_to_int64 (i); - q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0); - return q; -} - -cairo_uint128_t -_cairo_uint64_to_uint128 (cairo_uint64_t i) -{ - cairo_uint128_t q; - - q.lo = i; - q.hi = _cairo_uint32_to_uint64 (0); - return q; -} - -cairo_int128_t -_cairo_int64_to_int128 (cairo_int64_t i) -{ - cairo_int128_t q; - - q.lo = i; - q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0); - return q; -} - -cairo_uint128_t -_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b) -{ - cairo_uint128_t s; - - s.hi = _cairo_uint64_add (a.hi, b.hi); - s.lo = _cairo_uint64_add (a.lo, b.lo); - if (_cairo_uint64_lt (s.lo, a.lo)) - s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1)); - return s; -} - -cairo_uint128_t -_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b) -{ - cairo_uint128_t s; - - s.hi = _cairo_uint64_sub (a.hi, b.hi); - s.lo = _cairo_uint64_sub (a.lo, b.lo); - if (_cairo_uint64_gt (s.lo, a.lo)) - s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1)); - return s; -} - -#if HAVE_UINT64_T - -#define uint64_lo32(i) ((i) & 0xffffffff) -#define uint64_hi32(i) ((i) >> 32) -#define uint64_lo(i) ((i) & 0xffffffff) -#define uint64_hi(i) ((i) >> 32) -#define uint64_shift32(i) ((i) << 32) -#define uint64_carry32 (((uint64_t) 1) << 32) - -#else - -#define uint64_lo32(i) ((i).lo) -#define uint64_hi32(i) ((i).hi) - -static const cairo_uint64_t -uint64_lo (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = i.lo; - s.hi = 0; - return s; -} - -static const cairo_uint64_t -uint64_hi (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = i.hi; - s.hi = 0; - return s; -} - -static const cairo_uint64_t -uint64_shift32 (cairo_uint64_t i) -{ - cairo_uint64_t s; - - s.lo = 0; - s.hi = i.lo; - return s; -} - -static const cairo_uint64_t uint64_carry32 = { 0, 1 }; - -#endif - -cairo_uint128_t -_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b) -{ - cairo_uint128_t s; - uint32_t ah, al, bh, bl; - cairo_uint64_t r0, r1, r2, r3; - - al = uint64_lo32 (a); - ah = uint64_hi32 (a); - bl = uint64_lo32 (b); - bh = uint64_hi32 (b); - - r0 = _cairo_uint32x32_64_mul (al, bl); - r1 = _cairo_uint32x32_64_mul (al, bh); - r2 = _cairo_uint32x32_64_mul (ah, bl); - r3 = _cairo_uint32x32_64_mul (ah, bh); - - r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */ - r1 = _cairo_uint64_add (r1, r2); /* but this can carry */ - if (_cairo_uint64_lt (r1, r2)) /* check */ - r3 = _cairo_uint64_add (r3, uint64_carry32); - - s.hi = _cairo_uint64_add (r3, uint64_hi(r1)); - s.lo = _cairo_uint64_add (uint64_shift32 (r1), - uint64_lo (r0)); - return s; -} - -cairo_int128_t -_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b) -{ - cairo_int128_t s; - s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a), - _cairo_int64_to_uint64(b)); - if (_cairo_int64_negative (a)) - s.hi = _cairo_uint64_sub (s.hi, - _cairo_int64_to_uint64 (b)); - if (_cairo_int64_negative (b)) - s.hi = _cairo_uint64_sub (s.hi, - _cairo_int64_to_uint64 (a)); - return s; -} - -cairo_uint128_t -_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b) -{ - cairo_uint128_t s; - - s = _cairo_uint64x64_128_mul (a.lo, b.lo); - s.hi = _cairo_uint64_add (s.hi, - _cairo_uint64_mul (a.lo, b.hi)); - s.hi = _cairo_uint64_add (s.hi, - _cairo_uint64_mul (a.hi, b.lo)); - return s; -} - -cairo_uint128_t -_cairo_uint128_lsl (cairo_uint128_t a, int shift) -{ - if (shift >= 64) - { - a.hi = a.lo; - a.lo = _cairo_uint32_to_uint64 (0); - shift -= 64; - } - if (shift) - { - a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift), - _cairo_uint64_rsl (a.lo, (64 - shift))); - a.lo = _cairo_uint64_lsl (a.lo, shift); - } - return a; -} - -cairo_uint128_t -_cairo_uint128_rsl (cairo_uint128_t a, int shift) -{ - if (shift >= 64) - { - a.lo = a.hi; - a.hi = _cairo_uint32_to_uint64 (0); - shift -= 64; - } - if (shift) - { - a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), - _cairo_uint64_lsl (a.hi, (64 - shift))); - a.hi = _cairo_uint64_rsl (a.hi, shift); - } - return a; -} - -cairo_uint128_t -_cairo_uint128_rsa (cairo_int128_t a, int shift) -{ - if (shift >= 64) - { - a.lo = a.hi; - a.hi = _cairo_uint64_rsa (a.hi, 64-1); - shift -= 64; - } - if (shift) - { - a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), - _cairo_uint64_lsl (a.hi, (64 - shift))); - a.hi = _cairo_uint64_rsa (a.hi, shift); - } - return a; -} - -int -_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b) -{ - return (_cairo_uint64_lt (a.hi, b.hi) || - (_cairo_uint64_eq (a.hi, b.hi) && - _cairo_uint64_lt (a.lo, b.lo))); -} - -int -_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b) -{ - if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) - return 1; - if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) - return 0; - return _cairo_uint128_lt (a, b); -} - -int -_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b) -{ - return (_cairo_uint64_eq (a.hi, b.hi) && - _cairo_uint64_eq (a.lo, b.lo)); -} - -/* - * The design of this algorithm comes from GCC, - * but the actual implementation is new - */ - -/* - * den >= num.hi - */ -static cairo_uquorem64_t -_cairo_uint128x64_normalized_divrem (cairo_uint128_t num, cairo_uint64_t den) -{ - cairo_uquorem64_t qr64; - cairo_uquorem64_t qr; - uint32_t q0, q1; - cairo_uint64_t r0, r1; - uint32_t d0, d1; - cairo_uint64_t t; - - d0 = uint64_lo32 (den); - d1 = uint64_hi32 (den); - - qr64 = _cairo_uint64_divrem (num.hi, _cairo_uint32_to_uint64 (d1)); - q1 = _cairo_uint64_to_uint32 (qr64.quo); - r1 = qr64.rem; - - t = _cairo_uint32x32_64_mul (q1, d0); - - r1 = _cairo_uint64_add (_cairo_uint64_lsl (r1, 32), - _cairo_uint64_rsl (num.lo, 32)); - - if (_cairo_uint64_lt (r1, t)) - { - q1--; - r1 = _cairo_uint64_add (r1, den); - if (_cairo_uint64_ge (r1, den) && _cairo_uint64_lt (r1, t)) - { - q1--; - r1 = _cairo_uint64_add (r1, den); - } - } - - r1 = _cairo_uint64_sub (r1, t); - - qr64 = _cairo_uint64_divrem (r1, _cairo_uint32_to_uint64 (d1)); - - q0 = _cairo_uint64_to_uint32 (qr64.quo); - r0 = qr64.rem; - - t = _cairo_uint32x32_64_mul (q0, d0); - - r0 = _cairo_uint64_add (_cairo_uint64_lsl (r0, 32), - _cairo_uint32_to_uint64 (_cairo_uint64_to_uint32 (num.lo))); - if (_cairo_uint64_lt (r0, t)) - { - q0--; - r0 = _cairo_uint64_add (r0, den); - if (_cairo_uint64_ge (r0, den) && _cairo_uint64_lt (r0, t)) - { - q0--; - r0 = _cairo_uint64_add (r0, den); - } - } - - r0 = _cairo_uint64_sub (r0, t); - - qr.quo = _cairo_uint32s_to_uint64 (q1, q0); - qr.rem = r0; - return qr; -} - -#if HAVE_UINT64_T - -static int -_cairo_leading_zeros64 (cairo_uint64_t q) -{ - int top = 0; - - if (q >= (uint64_t) 0x10000 << 16) - { - top += 32; - q >>= 32; - } - if (q >= (uint64_t) 0x10000) - { - top += 16; - q >>= 16; - } - if (q >= (uint64_t) 0x100) - { - top += 8; - q >>= 8; - } - top += top_bit [q]; - return 64 - top; -} - -#else - -static const int -_cairo_leading_zeros64 (cairo_uint64_t d) -{ - if (d.hi) - return _cairo_leading_zeros32 (d.hi); - else - return 32 + _cairo_leading_zeros32 (d.lo); -} - -#endif - -cairo_uquorem128_t -_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) -{ - cairo_uquorem64_t qr64; - cairo_uquorem128_t qr; - int norm; - cairo_uint64_t q1, q0, r1, r0; - - if (_cairo_uint64_eq (den.hi, _cairo_uint32_to_uint64 (0))) - { - if (_cairo_uint64_gt (den.lo, num.hi)) - { - /* 0q = nn / 0d */ - - norm = _cairo_leading_zeros64 (den.lo); - if (norm) - { - den.lo = _cairo_uint64_lsl (den.lo, norm); - num = _cairo_uint128_lsl (num, norm); - } - q1 = _cairo_uint32_to_uint64 (0); - } - else - { - /* qq = NN / 0d */ - - if (_cairo_uint64_eq (den.lo, _cairo_uint32_to_uint64 (0))) - den.lo = _cairo_uint64_divrem (_cairo_uint32_to_uint64 (1), - den.lo).quo; - - norm = _cairo_leading_zeros64 (den.lo); - if (norm) - { - cairo_uint128_t num1; - - den.lo = _cairo_uint64_lsl (den.lo, norm); - num1 = _cairo_uint128_rsl (num, 64 - norm); - qr64 = _cairo_uint128x64_normalized_divrem (num1, den.lo); - q1 = qr64.quo; - num.hi = qr64.rem; - num.lo = _cairo_uint64_lsl (num.lo, norm); - } - else - { - num.hi = _cairo_uint64_sub (num.hi, den.lo); - q1 = _cairo_uint32_to_uint64 (1); - } - } - qr64 = _cairo_uint128x64_normalized_divrem (num, den.lo); - q0 = qr64.quo; - r1 = _cairo_uint32_to_uint64 (0); - r0 = _cairo_uint64_rsl (qr64.rem, norm); - } - else - { - if (_cairo_uint64_gt (den.hi, num.hi)) - { - /* 00 = nn / DD */ - q0 = q1 = _cairo_uint32_to_uint64 (0); - r0 = num.lo; - r1 = num.hi; - } - else - { - /* 0q = NN / dd */ - - norm = _cairo_leading_zeros64 (den.hi); - if (norm == 0) - { - if (_cairo_uint64_gt (num.hi, den.hi) || - _cairo_uint64_ge (num.lo, den.lo)) - { - q0 = _cairo_uint32_to_uint64 (1); - num = _cairo_uint128_sub (num, den); - } - else - q0 = _cairo_uint32_to_uint64 (0); - - q1 = _cairo_uint32_to_uint64 (0); - r0 = num.lo; - r1 = num.hi; - } - else - { - cairo_uint128_t num1; - cairo_uint128_t part; - - num1 = _cairo_uint128_rsl (num, 64 - norm); - den = _cairo_uint128_lsl (den, norm); - - qr64 = _cairo_uint128x64_normalized_divrem (num1, den.hi); - part = _cairo_uint64x64_128_mul (qr64.quo, den.lo); - - q0 = qr64.quo; - - num.lo = _cairo_uint64_lsl (num.lo, norm); - num.hi = qr64.rem; - - if (_cairo_uint128_gt (part, num)) - { - q0 = _cairo_uint64_sub (q0, _cairo_uint32_to_uint64 (1)); - part = _cairo_uint128_sub (part, den); - } - - q1 = _cairo_uint32_to_uint64 (0); - - num = _cairo_uint128_sub (num, part); - num = _cairo_uint128_rsl (num, norm); - r0 = num.lo; - r1 = num.hi; - } - } - } - qr.quo.lo = q0; - qr.quo.hi = q1; - qr.rem.lo = r0; - qr.rem.hi = r1; - return qr; -} - -cairo_int128_t -_cairo_int128_negate (cairo_int128_t a) -{ - a.lo = _cairo_uint64_not (a.lo); - a.hi = _cairo_uint64_not (a.hi); - return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1)); -} - -cairo_int128_t -_cairo_int128_not (cairo_int128_t a) -{ - a.lo = _cairo_uint64_not (a.lo); - a.hi = _cairo_uint64_not (a.hi); - return a; -} - -#endif /* !HAVE_UINT128_T */ - -cairo_quorem128_t -_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den) -{ - int num_neg = _cairo_int128_negative (num); - int den_neg = _cairo_int128_negative (den); - cairo_uquorem128_t uqr; - cairo_quorem128_t qr; - - if (num_neg) - num = _cairo_int128_negate (num); - if (den_neg) - den = _cairo_int128_negate (den); - uqr = _cairo_uint128_divrem (num, den); - if (num_neg) - qr.rem = _cairo_int128_negate (uqr.rem); - else - qr.rem = uqr.rem; - if (num_neg != den_neg) - qr.quo = _cairo_int128_negate (uqr.quo); - else - qr.quo = uqr.quo; - return qr; -} diff --git a/src/cairo_win32_font.c b/src/cairo_win32_font.c deleted file mode 100644 index 02f0cffd6..000000000 --- a/src/cairo_win32_font.c +++ /dev/null @@ -1,1252 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * - * 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. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - */ - -#include <string.h> -#include <stdio.h> - -#include "cairo-win32-private.h" - -#ifndef SPI_GETFONTSMOOTHINGTYPE -#define SPI_GETFONTSMOOTHINGTYPE 0x200a -#endif -#ifndef FE_FONTSMOOTHINGCLEARTYPE -#define FE_FONTSMOOTHINGCLEARTYPE 2 -#endif -#ifndef CLEARTYPE_QUALITY -#define CLEARTYPE_QUALITY 5 -#endif - -const cairo_font_backend_t cairo_win32_font_backend; - -#define LOGICAL_SCALE 32 - -typedef struct { - cairo_font_t base; - - LOGFONTW logfont; - - BYTE quality; - - /* We do drawing and metrics computation in a "logical space" which - * is similar to font space, except that it is scaled by a factor - * of the (desired font size) * (LOGICAL_SCALE). The multiplication - * by LOGICAL_SCALE allows for sub-pixel precision. - */ - double logical_scale; - - /* The size we should actually request the font at from Windows; differs - * from the logical_scale because it is quantized for orthogonal - * transformations - */ - double logical_size; - - /* Transformations from device <=> logical space - */ - cairo_matrix_t logical_to_device; - cairo_matrix_t device_to_logical; - - /* We special case combinations of 90-degree-rotations, scales and - * flips ... that is transformations that take the axes to the - * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y - * encode the 8 possibilities for orientation (4 rotation angles with - * and without a flip), and scale_x, scale_y the scale components. - */ - cairo_bool_t preserve_axes; - cairo_bool_t swap_axes; - cairo_bool_t swap_x; - cairo_bool_t swap_y; - double x_scale; - double y_scale; - - /* The size of the design unit of the font - */ - int em_square; - - HFONT scaled_font; - HFONT unscaled_font; - -} cairo_win32_font_t; - -#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) - -static void -_compute_transform (cairo_win32_font_t *font, - cairo_font_scale_t *sc) -{ - if (NEARLY_ZERO (sc->matrix[0][1]) && NEARLY_ZERO (sc->matrix[1][0])) { - font->preserve_axes = TRUE; - font->x_scale = sc->matrix[0][0]; - font->swap_x = (sc->matrix[0][0] < 0); - font->y_scale = sc->matrix[1][1]; - font->swap_y = (sc->matrix[1][1] < 0); - font->swap_axes = FALSE; - - } else if (NEARLY_ZERO (sc->matrix[0][0]) && NEARLY_ZERO (sc->matrix[1][1])) { - font->preserve_axes = TRUE; - font->x_scale = sc->matrix[0][1]; - font->swap_x = (sc->matrix[0][1] < 0); - font->y_scale = sc->matrix[1][0]; - font->swap_y = (sc->matrix[1][0] < 0); - font->swap_axes = TRUE; - - } else { - font->preserve_axes = FALSE; - font->swap_x = font->swap_y = font->swap_axes = FALSE; - } - - if (font->preserve_axes) { - if (font->swap_x) - font->x_scale = - font->x_scale; - if (font->swap_y) - font->y_scale = - font->y_scale; - - font->logical_scale = LOGICAL_SCALE * font->y_scale; - font->logical_size = LOGICAL_SCALE * floor (font->y_scale + 0.5); - } - - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. - */ - cairo_matrix_set_affine (&font->logical_to_device, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - if (!font->preserve_axes) { - _cairo_matrix_compute_scale_factors (&font->logical_to_device, - &font->x_scale, &font->y_scale, - TRUE); /* XXX: Handle vertical text */ - - font->logical_size = floor (LOGICAL_SCALE * font->y_scale + 0.5); - font->logical_scale = LOGICAL_SCALE * font->y_scale; - } - - cairo_matrix_scale (&font->logical_to_device, - 1.0 / font->logical_scale, 1.0 / font->logical_scale); - - font->device_to_logical = font->logical_to_device; - if (!CAIRO_OK (cairo_matrix_invert (&font->device_to_logical))) - cairo_matrix_set_identity (&font->device_to_logical); -} - -static BYTE -_get_system_quality (void) -{ - BOOL font_smoothing; - - if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { - _cairo_win32_print_gdi_error ("_get_system_quality"); - return FALSE; - } - - if (font_smoothing) { - OSVERSIONINFO version_info; - - version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); - - if (!GetVersionEx (&version_info)) { - _cairo_win32_print_gdi_error ("_get_system_quality"); - return FALSE; - } - - if (version_info.dwMajorVersion > 5 || - (version_info.dwMajorVersion == 5 && - version_info.dwMinorVersion >= 1)) { /* XP or newer */ - UINT smoothing_type; - - if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, - 0, &smoothing_type, 0)) { - _cairo_win32_print_gdi_error ("_get_system_quality"); - return FALSE; - } - - if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) - return CLEARTYPE_QUALITY; - } - - return ANTIALIASED_QUALITY; - } else - return DEFAULT_QUALITY; -} - -static cairo_font_t * -_win32_font_create (LOGFONTW *logfont, - cairo_font_scale_t *scale) -{ - cairo_win32_font_t *f; - - f = malloc (sizeof(cairo_win32_font_t)); - if (f == NULL) - return NULL; - - f->logfont = *logfont; - f->quality = _get_system_quality (); - f->em_square = 0; - f->scaled_font = NULL; - f->unscaled_font = NULL; - - _compute_transform (f, scale); - - _cairo_font_init ((cairo_font_t *)f, scale, &cairo_win32_font_backend); - - return (cairo_font_t *)f; -} - -static cairo_status_t -_win32_font_set_world_transform (cairo_win32_font_t *font, - HDC hdc) -{ - XFORM xform; - - xform.eM11 = font->logical_to_device.m[0][0]; - xform.eM21 = font->logical_to_device.m[1][0]; - xform.eM12 = font->logical_to_device.m[0][1]; - xform.eM22 = font->logical_to_device.m[1][1]; - xform.eDx = font->logical_to_device.m[2][0]; - xform.eDy = font->logical_to_device.m[2][1]; - - if (!SetWorldTransform (hdc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_font_set_world_transform"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_win32_font_set_identity_transform (HDC hdc) -{ - if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_win32_font_set_identity_transform"); - - return CAIRO_STATUS_SUCCESS; -} - -static HDC -_get_global_font_dc (void) -{ - static HDC hdc; - - if (!hdc) { - hdc = CreateCompatibleDC (NULL); - if (!hdc) { - _cairo_win32_print_gdi_error ("_get_global_font_dc"); - return NULL; - } - - if (!SetGraphicsMode (hdc, GM_ADVANCED)) { - _cairo_win32_print_gdi_error ("_get_global_font_dc"); - DeleteDC (hdc); - return NULL; - } - } - - return hdc; -} - -static HFONT -_win32_font_get_scaled_font (cairo_win32_font_t *font) -{ - if (!font->scaled_font) { - LOGFONTW logfont = font->logfont; - logfont.lfHeight = font->logical_size; - logfont.lfWidth = 0; - logfont.lfEscapement = 0; - logfont.lfOrientation = 0; - logfont.lfQuality = font->quality; - - font->scaled_font = CreateFontIndirectW (&logfont); - if (!font->scaled_font) { - _cairo_win32_print_gdi_error ("_win32_font_get_scaled_font"); - return NULL; - } - } - - return font->scaled_font; -} - -static HFONT -_win32_font_get_unscaled_font (cairo_win32_font_t *font, - HDC hdc) -{ - if (!font->unscaled_font) { - OUTLINETEXTMETRIC *otm; - unsigned int otm_size; - HFONT scaled_font; - LOGFONTW logfont; - - scaled_font = _win32_font_get_scaled_font (font); - if (!scaled_font) - return NULL; - - if (!SelectObject (hdc, scaled_font)) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:SelectObject"); - return NULL; - } - - otm_size = GetOutlineTextMetrics (hdc, 0, NULL); - if (!otm_size) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); - return NULL; - } - - otm = malloc (otm_size); - if (!otm) - return NULL; - - if (!GetOutlineTextMetrics (hdc, otm_size, otm)) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); - free (otm); - return NULL; - } - - font->em_square = otm->otmEMSquare; - free (otm); - - logfont = font->logfont; - logfont.lfHeight = font->em_square; - logfont.lfWidth = 0; - logfont.lfEscapement = 0; - logfont.lfOrientation = 0; - logfont.lfQuality = font->quality; - - font->unscaled_font = CreateFontIndirectW (&logfont); - if (!font->unscaled_font) { - _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:CreateIndirect"); - return NULL; - } - } - - return font->unscaled_font; -} - -static cairo_status_t -_cairo_win32_font_select_unscaled_font (cairo_font_t *font, - HDC hdc) -{ - cairo_status_t status; - HFONT hfont; - HFONT old_hfont = NULL; - - hfont = _win32_font_get_unscaled_font ((cairo_win32_font_t *)font, hdc); - if (!hfont) - return CAIRO_STATUS_NO_MEMORY; - - old_hfont = SelectObject (hdc, hfont); - if (!old_hfont) - return _cairo_win32_print_gdi_error ("_cairo_win32_font_select_unscaled_font"); - - status = _win32_font_set_identity_transform (hdc); - if (!CAIRO_OK (status)) { - SelectObject (hdc, old_hfont); - return status; - } - - SetMapMode (hdc, MM_TEXT); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_font_done_unscaled_font (cairo_font_t *font) -{ -} - -/* implement the font backend interface */ - -static cairo_status_t -_cairo_win32_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *scale, - cairo_font_t **font_out) -{ - LOGFONTW logfont; - cairo_font_t *font; - uint16_t *face_name; - int face_name_len; - cairo_status_t status; - - status = _cairo_utf8_to_utf16 (family, -1, &face_name, &face_name_len); - if (!CAIRO_OK (status)) - return status; - - if (face_name_len > LF_FACESIZE - 1) { - free (face_name); - return CAIRO_STATUS_INVALID_STRING; - } - - memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * (face_name_len + 1)); - free (face_name); - - logfont.lfHeight = 0; /* filled in later */ - logfont.lfWidth = 0; /* filled in later */ - logfont.lfEscapement = 0; /* filled in later */ - logfont.lfOrientation = 0; /* filled in later */ - - switch (weight) { - case CAIRO_FONT_WEIGHT_NORMAL: - default: - logfont.lfWeight = FW_NORMAL; - break; - case CAIRO_FONT_WEIGHT_BOLD: - logfont.lfWeight = FW_BOLD; - break; - } - - switch (slant) { - case CAIRO_FONT_SLANT_NORMAL: - default: - logfont.lfItalic = FALSE; - break; - case CAIRO_FONT_SLANT_ITALIC: - case CAIRO_FONT_SLANT_OBLIQUE: - logfont.lfItalic = TRUE; - break; - } - - logfont.lfUnderline = FALSE; - logfont.lfStrikeOut = FALSE; - /* The docs for LOGFONT discourage using this, since the - * interpretation is locale-specific, but it's not clear what - * would be a better alternative. - */ - logfont.lfCharSet = DEFAULT_CHARSET; - logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; - logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ - logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - - if (!logfont.lfFaceName) - return CAIRO_STATUS_NO_MEMORY; - - font = _win32_font_create (&logfont, scale); - if (!font) - return CAIRO_STATUS_NO_MEMORY; - - *font_out = font; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_font_destroy_font (void *abstract_font) -{ - cairo_win32_font_t *font = abstract_font; - - if (font->scaled_font) - DeleteObject (font->scaled_font); - - if (font->unscaled_font) - DeleteObject (font->unscaled_font); - - free (font); -} - -static void -_cairo_win32_font_destroy_unscaled_font (void *abstract_font) -{ -} - -static void -_cairo_win32_font_get_glyph_cache_key (void *abstract_font, - cairo_glyph_cache_key_t *key) -{ -} - -static cairo_status_t -_cairo_win32_font_text_to_glyphs (void *abstract_font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) -{ - cairo_win32_font_t *font = abstract_font; - uint16_t *utf16; - int n16; - GCP_RESULTSW gcp_results; - unsigned int buffer_size, i; - WCHAR *glyph_indices = NULL; - int *dx = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - double x_pos; - HDC hdc = NULL; - - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); - if (!CAIRO_OK (status)) - return status; - - gcp_results.lStructSize = sizeof (GCP_RESULTS); - gcp_results.lpOutString = NULL; - gcp_results.lpOrder = NULL; - gcp_results.lpCaretPos = NULL; - gcp_results.lpClass = NULL; - - buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ - if (buffer_size > INT_MAX) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL1; - } - - hdc = _get_global_font_dc (); - if (!hdc) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL1; - } - - status = cairo_win32_font_select_font (&font->base, hdc); - if (!CAIRO_OK (status)) - goto FAIL1; - - while (TRUE) { - if (glyph_indices) { - free (glyph_indices); - glyph_indices = NULL; - } - if (dx) { - free (dx); - dx = NULL; - } - - glyph_indices = malloc (sizeof (WCHAR) * buffer_size); - dx = malloc (sizeof (int) * buffer_size); - if (!glyph_indices || !dx) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - - gcp_results.nGlyphs = buffer_size; - gcp_results.lpDx = dx; - gcp_results.lpGlyphs = glyph_indices; - - if (!GetCharacterPlacementW (hdc, utf16, n16, - 0, - &gcp_results, - GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_font_text_to_glyphs"); - goto FAIL2; - } - - if (gcp_results.lpDx && gcp_results.lpGlyphs) - break; - - /* Too small a buffer, try again */ - - buffer_size *= 1.5; - if (buffer_size > INT_MAX) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - } - - *num_glyphs = gcp_results.nGlyphs; - *glyphs = malloc (sizeof (cairo_glyph_t) * gcp_results.nGlyphs); - if (!*glyphs) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - - x_pos = 0; - for (i = 0; i < gcp_results.nGlyphs; i++) { - (*glyphs)[i].index = glyph_indices[i]; - (*glyphs)[i].x = x_pos ; - (*glyphs)[i].y = 0; - - x_pos += dx[i] / font->logical_scale; - } - - FAIL2: - if (glyph_indices) - free (glyph_indices); - if (dx) - free (dx); - - cairo_win32_font_done_font (&font->base); - - FAIL1: - free (utf16); - - return status; -} - -static cairo_status_t -_cairo_win32_font_font_extents (void *abstract_font, - cairo_font_extents_t *extents) -{ - cairo_win32_font_t *font = abstract_font; - cairo_status_t status; - TEXTMETRIC metrics; - HDC hdc; - - hdc = _get_global_font_dc (); - if (!hdc) - return CAIRO_STATUS_NO_MEMORY; - - if (font->preserve_axes) { - /* For 90-degree rotations (including 0), we get the metrics - * from the GDI in logical space, then convert back to font space - */ - status = cairo_win32_font_select_font (&font->base, hdc); - if (!CAIRO_OK (status)) - return status; - GetTextMetrics (hdc, &metrics); - cairo_win32_font_done_font (&font->base); - - extents->ascent = metrics.tmAscent / font->logical_scale; - extents->descent = metrics.tmDescent / font->logical_scale; - - extents->height = (metrics.tmHeight + metrics.tmExternalLeading) / font->logical_scale; - extents->max_x_advance = metrics.tmMaxCharWidth / font->logical_scale; - extents->max_y_advance = 0; - - } else { - /* For all other transformations, we use the design metrics - * of the font. The GDI results from GetTextMetrics() on a - * transformed font are inexplicably large and we want to - * avoid them. - */ - status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); - if (!CAIRO_OK (status)) - return status; - GetTextMetrics (hdc, &metrics); - _cairo_win32_font_done_unscaled_font (&font->base); - - extents->ascent = (double)metrics.tmAscent / font->em_square; - extents->descent = metrics.tmDescent * font->em_square; - extents->height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / font->em_square; - extents->max_x_advance = (double)(metrics.tmMaxCharWidth) / font->em_square; - extents->max_y_advance = 0; - - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_font_glyph_extents (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_win32_font_t *font = abstract_font; - static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; - GLYPHMETRICS metrics; - cairo_status_t status; - HDC hdc; - - hdc = _get_global_font_dc (); - if (!hdc) - return CAIRO_STATUS_NO_MEMORY; - - /* We handle only the case num_glyphs == 1, glyphs[i].x == glyphs[0].y == 0. - * This is all that the calling code triggers, and the backend interface - * will eventually be changed to match - */ - assert (num_glyphs == 1); - - if (font->preserve_axes) { - /* If we aren't rotating / skewing the axes, then we get the metrics - * from the GDI in device space and convert to font space. - */ - status = cairo_win32_font_select_font (&font->base, hdc); - if (!CAIRO_OK (status)) - return status; - GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix); - cairo_win32_font_done_font (&font->base); - - if (font->swap_axes) { - extents->x_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; - extents->y_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; - extents->width = metrics.gmBlackBoxY / font->y_scale; - extents->height = metrics.gmBlackBoxX / font->x_scale; - extents->x_advance = metrics.gmCellIncY / font->x_scale; - extents->y_advance = metrics.gmCellIncX / font->y_scale; - } else { - extents->x_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; - extents->y_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; - extents->width = metrics.gmBlackBoxX / font->x_scale; - extents->height = metrics.gmBlackBoxY / font->y_scale; - extents->x_advance = metrics.gmCellIncX / font->x_scale; - extents->y_advance = metrics.gmCellIncY / font->y_scale; - } - - if (font->swap_x) { - extents->x_bearing = (- extents->x_bearing - extents->width); - extents->x_advance = - extents->x_advance; - } - - if (font->swap_y) { - extents->y_bearing = (- extents->y_bearing - extents->height); - extents->y_advance = - extents->y_advance; - } - - } else { - /* For all other transformations, we use the design metrics - * of the font. - */ - status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); - GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix); - _cairo_win32_font_done_unscaled_font (&font->base); - - extents->x_bearing = (double)metrics.gmptGlyphOrigin.x / font->em_square; - extents->y_bearing = (double)metrics.gmptGlyphOrigin.y / font->em_square; - extents->width = (double)metrics.gmBlackBoxX / font->em_square; - extents->height = (double)metrics.gmBlackBoxY / font->em_square; - extents->x_advance = (double)metrics.gmCellIncX / font->em_square; - extents->y_advance = (double)metrics.gmCellIncY / font->em_square; - } - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_win32_font_glyph_bbox (void *abstract_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; - cairo_win32_font_t *font = abstract_font; - int x1 = 0, x2 = 0, y1 = 0, y2 = 0; - - if (num_glyphs > 0) { - HDC hdc = _get_global_font_dc (); - GLYPHMETRICS metrics; - cairo_status_t status; - int i; - - if (!hdc) - return CAIRO_STATUS_NO_MEMORY; - - status = cairo_win32_font_select_font (&font->base, hdc); - if (!CAIRO_OK (status)) - return status; - - for (i = 0; i < num_glyphs; i++) { - int x = floor (0.5 + glyphs[i].x); - int y = floor (0.5 + glyphs[i].y); - - GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix); - - if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) - x1 = x + metrics.gmptGlyphOrigin.x; - if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) - y1 = y - metrics.gmptGlyphOrigin.y; - if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + metrics.gmBlackBoxX) - x2 = x + metrics.gmptGlyphOrigin.x + metrics.gmBlackBoxX; - if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY) - y2 = y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY; - } - - cairo_win32_font_done_font (&font->base); - } - - bbox->p1.x = _cairo_fixed_from_int (x1); - bbox->p1.y = _cairo_fixed_from_int (y1); - bbox->p2.x = _cairo_fixed_from_int (x2); - bbox->p2.y = _cairo_fixed_from_int (y2); - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct { - cairo_win32_font_t *font; - HDC hdc; - - cairo_array_t glyphs; - cairo_array_t dx; - - int start_x; - int last_x; - int last_y; -} cairo_glyph_state_t; - -static void -_start_glyphs (cairo_glyph_state_t *state, - cairo_win32_font_t *font, - HDC hdc) -{ - state->hdc = hdc; - state->font = font; - - _cairo_array_init (&state->glyphs, sizeof (WCHAR)); - _cairo_array_init (&state->dx, sizeof (int)); -} - -static cairo_status_t -_flush_glyphs (cairo_glyph_state_t *state) -{ - int dx = 0; - if (!_cairo_array_append (&state->dx, &dx, 1)) - return CAIRO_STATUS_NO_MEMORY; - - if (!ExtTextOutW (state->hdc, - state->start_x, state->last_y, - ETO_GLYPH_INDEX, - NULL, - (WCHAR *)state->glyphs.elements, - state->glyphs.num_elements, - (int *)state->dx.elements)) { - return _cairo_win32_print_gdi_error ("_flush_glyphs"); - } - - _cairo_array_truncate (&state->glyphs, 0); - _cairo_array_truncate (&state->dx, 0); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_add_glyph (cairo_glyph_state_t *state, - unsigned long index, - double device_x, - double device_y) -{ - double user_x = device_x; - double user_y = device_y; - WCHAR glyph_index = index; - int logical_x, logical_y; - - cairo_matrix_transform_point (&state->font->device_to_logical, &user_x, &user_y); - - logical_x = floor (user_x + 0.5); - logical_y = floor (user_y + 0.5); - - if (state->glyphs.num_elements > 0) { - int dx; - - if (logical_y != state->last_y) { - cairo_status_t status = _flush_glyphs (state); - if (!CAIRO_OK (status)) - return status; - state->start_x = logical_x; - } - - dx = logical_x - state->last_x; - if (!_cairo_array_append (&state->dx, &dx, 1)) - return CAIRO_STATUS_NO_MEMORY; - } else { - state->start_x = logical_x; - } - - state->last_x = logical_x; - state->last_y = logical_y; - - _cairo_array_append (&state->glyphs, &glyph_index, 1); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_finish_glyphs (cairo_glyph_state_t *state) -{ - _flush_glyphs (state); - - _cairo_array_fini (&state->glyphs); - _cairo_array_fini (&state->dx); -} - -static cairo_status_t -_draw_glyphs_on_surface (cairo_win32_surface_t *surface, - cairo_win32_font_t *font, - COLORREF color, - int x_offset, - int y_offset, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_glyph_state_t state; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - int i; - - if (!SaveDC (surface->dc)) - return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); - - status = cairo_win32_font_select_font (&font->base, surface->dc); - if (!CAIRO_OK (status)) - goto FAIL1; - - SetTextColor (surface->dc, color); - SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); - SetBkMode (surface->dc, TRANSPARENT); - - _start_glyphs (&state, font, surface->dc); - - for (i = 0; i < num_glyphs; i++) { - status = _add_glyph (&state, glyphs[i].index, - glyphs[i].x - x_offset, glyphs[i].y - y_offset); - if (!CAIRO_OK (status)) - goto FAIL2; - } - - FAIL2: - _finish_glyphs (&state); - cairo_win32_font_done_font (&font->base); - FAIL1: - RestoreDC (surface->dc, 1); - - return status; -} - -/* Duplicate the green channel of a 4-channel mask in the alpha channel, then - * invert the whole mask. - */ -static void -_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; - int i, j; - - for (i = 0; i < image->height; i++) { - uint32_t *p = (uint32_t *) (image->data + i * image->stride); - for (j = 0; j < image->width; j++) { - *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); - p++; - } - } -} - -/* Invert a mask - */ -static void -_invert_argb32_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; - int i, j; - - for (i = 0; i < image->height; i++) { - uint32_t *p = (uint32_t *) (image->data + i * image->stride); - for (j = 0; j < image->width; j++) { - *p = 0xffffffff ^ *p; - p++; - } - } -} - -/* Compute an alpha-mask from a monochrome RGB24 image - */ -static cairo_surface_t * -_compute_a8_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; - cairo_image_surface_t *image8; - int i, j; - - image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, - image24->width, image24->height); - if (!image8) - return NULL; - - for (i = 0; i < image24->height; i++) { - uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); - unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); - - for (j = 0; j < image24->width; j++) { - *q = 255 - ((*p & 0x0000ff00) >> 8); - p++; - q++; - } - } - - return &image8->base; -} - -static cairo_status_t -_cairo_win32_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *generic_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_win32_font_t *font = abstract_font; - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; - cairo_status_t status; - - if (width == 0 || height == 0) - return CAIRO_STATUS_SUCCESS; - - if (_cairo_surface_is_win32 (generic_surface) && - surface->format == CAIRO_FORMAT_RGB24 && - operator == CAIRO_OPERATOR_OVER && - pattern->type == CAIRO_PATTERN_SOLID && - _cairo_pattern_is_opaque (pattern)) { - - cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; - - /* When compositing OVER on a GDI-understood surface, with a - * solid opaque color, we can just call ExtTextOut directly. - */ - COLORREF new_color; - - new_color = RGB (((int)(0xffff * solid_pattern->red)) >> 8, - ((int)(0xffff * solid_pattern->green)) >> 8, - ((int)(0xffff * solid_pattern->blue)) >> 8); - - status = _draw_glyphs_on_surface (surface, font, new_color, - 0, 0, - glyphs, num_glyphs); - - return status; - } else { - /* Otherwise, we need to draw using software fallbacks. We create a mask - * surface by drawing the the glyphs onto a DIB, black-on-white then - * inverting. GDI outputs gamma-corrected images so inverted black-on-white - * is very different from white-on-black. We favor the more common - * case where the final output is dark-on-light. - */ - cairo_win32_surface_t *tmp_surface; - cairo_surface_t *mask_surface; - cairo_surface_pattern_t mask; - RECT r; - - tmp_surface = (cairo_win32_surface_t *)_cairo_win32_surface_create_dib (CAIRO_FORMAT_ARGB32, width, height); - if (!tmp_surface) - return CAIRO_STATUS_NO_MEMORY; - - r.left = 0; - r.top = 0; - r.right = width; - r.bottom = height; - FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); - - _draw_glyphs_on_surface (tmp_surface, font, RGB (0, 0, 0), - dest_x, dest_y, - glyphs, num_glyphs); - - if (font->quality == CLEARTYPE_QUALITY) { - /* For ClearType, we need a 4-channel mask. If we are compositing on - * a surface with alpha, we need to compute the alpha channel of - * the mask (we just copy the green channel). But for a destination - * surface without alpha the alpha channel of the mask is ignored - */ - - if (surface->format != CAIRO_FORMAT_RGB24) - _compute_argb32_mask_alpha (tmp_surface); - else - _invert_argb32_mask (tmp_surface); - - mask_surface = &tmp_surface->base; - - /* XXX: Hacky, should expose this in cairo_image_surface */ - pixman_image_set_component_alpha (((cairo_image_surface_t *)tmp_surface->image)->pixman_image, TRUE); - - } else { - mask_surface = _compute_a8_mask (tmp_surface); - cairo_surface_destroy (&tmp_surface->base); - if (!mask_surface) - return CAIRO_STATUS_NO_MEMORY; - } - - /* For operator == OVER, no-cleartype, a possible optimization here is to - * draw onto an intermediate ARGB32 surface and alpha-blend that with the - * destination - */ - _cairo_pattern_init_for_surface (&mask, mask_surface); - - status = _cairo_surface_composite (operator, pattern, - &mask.base, - &surface->base, - source_x, source_y, - 0, 0, - dest_x, dest_y, - width, height); - - _cairo_pattern_fini (&mask.base); - - cairo_surface_destroy (mask_surface); - - return status; - } -} - -static cairo_status_t -_cairo_win32_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) -{ - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_font_create_glyph (cairo_image_glyph_cache_entry_t *val) -{ - return CAIRO_STATUS_NO_MEMORY; -} - -const cairo_font_backend_t cairo_win32_font_backend = { - _cairo_win32_font_create, - _cairo_win32_font_destroy_font, - _cairo_win32_font_destroy_unscaled_font, - _cairo_win32_font_font_extents, - _cairo_win32_font_text_to_glyphs, - _cairo_win32_font_glyph_extents, - _cairo_win32_font_glyph_bbox, - _cairo_win32_font_show_glyphs, - _cairo_win32_font_glyph_path, - _cairo_win32_font_get_glyph_cache_key, - _cairo_win32_font_create_glyph -}; - -/* implement the platform-specific interface */ - -/** - * cairo_win32_font_create_for_logfontw: - * @logfont: A #LOGFONTW structure specifying the font to use. - * The lfHeight, lfWidth, lfOrientation and lfEscapement - * fields of this structure are ignored; information from - * @scale will be used instead. - * @scale: The scale at which this font will be used. The - * scale is given by multiplying the font matrix (see - * cairo_transform_font()) by the current transformation matrix. - * The translation elements of the resulting matrix are ignored. - * - * Creates a new font for the Win32 font backend based on a - * #LOGFONT. This font can then be used with - * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend - * specific functions like cairo_win32_font_select_font(). - * - * Return value: a newly created #cairo_font_t. Free with - * cairo_font_destroy() when you are done using it. - **/ -cairo_font_t * -cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, - cairo_matrix_t *scale) -{ - cairo_font_scale_t sc; - - cairo_matrix_get_affine (scale, - &sc.matrix[0][0], &sc.matrix[0][1], - &sc.matrix[1][0], &sc.matrix[1][1], - NULL, NULL); - - return _win32_font_create (logfont, &sc); -} - -/** - * cairo_win32_font_select_font: - * @font: A #cairo_font_t from the Win32 font backend. Such an - * object can be created with cairo_win32_font_create_for_logfontw(). - * @hdc: a device context - * - * Selects the font into the given device context and changes the - * map mode and world transformation of the device context to match - * that of the font. This function is intended for use when using - * layout APIs such as Uniscribe to do text layout with the - * Cairo font. After finishing using the device context, you must call - * cairo_win32_font_done_font() to release any resources allocated - * by this function. - * - * See cairo_win32_font_get_scale_factor() for converting logical - * coordinates from the device context to font space. - * - * Normally, calls to SaveDC() and RestoreDC() would be made around - * the use of this function to preserve the original graphics state. - * - * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. - * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and - * the device context is unchanged. - **/ -cairo_status_t -cairo_win32_font_select_font (cairo_font_t *font, - HDC hdc) -{ - cairo_status_t status; - HFONT hfont; - HFONT old_hfont = NULL; - int old_mode; - - hfont = _win32_font_get_scaled_font ((cairo_win32_font_t *)font); - if (!hfont) - return CAIRO_STATUS_NO_MEMORY; - - old_hfont = SelectObject (hdc, hfont); - if (!old_hfont) - return _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); - - old_mode = SetGraphicsMode (hdc, GM_ADVANCED); - if (!old_mode) { - status = _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); - SelectObject (hdc, old_hfont); - return status; - } - - status = _win32_font_set_world_transform ((cairo_win32_font_t *)font, hdc); - if (!CAIRO_OK (status)) { - SetGraphicsMode (hdc, old_mode); - SelectObject (hdc, old_hfont); - return status; - } - - SetMapMode (hdc, MM_TEXT); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_win32_font_done_font: - * @font: A #cairo_font_t from the Win32 font backend. - * - * Releases any resources allocated by cairo_win32_font_select_font() - **/ -void -cairo_win32_font_done_font (cairo_font_t *font) -{ -} - -/** - * cairo_win32_font_get_scale_factor: - * @font: a #cairo_font_t from the Win32 font backend - * - * Gets a scale factor between logical coordinates in the coordinate - * space used by cairo_win32_font_select_font() and font space coordinates. - * - * Return value: factor to multiply logical units by to get font space - * coordinates. - **/ -double -cairo_win32_font_get_scale_factor (cairo_font_t *font) -{ - return 1. / ((cairo_win32_font_t *)font)->logical_scale; -} diff --git a/src/cairo_win32_surface.c b/src/cairo_win32_surface.c deleted file mode 100644 index dcfe6d044..000000000 --- a/src/cairo_win32_surface.c +++ /dev/null @@ -1,931 +0,0 @@ -/* Cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc. - * - * 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. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Owen Taylor <otaylor@redhat.com> - */ - -#include <stdio.h> - -#include "cairo-win32-private.h" - -static const cairo_surface_backend_t cairo_win32_surface_backend; - -/** - * _cairo_win32_print_gdi_error: - * @context: context string to display along with the error - * - * Helper function to dump out a human readable form of the - * current error code. - * - * Return value: A Cairo status code for the error code - **/ -cairo_status_t -_cairo_win32_print_gdi_error (const char *context) -{ - void *lpMsgBuf; - DWORD last_error = GetLastError (); - - if (!FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - last_error, - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, NULL)) { - fprintf (stderr, "%s: Unknown GDI error", context); - } else { - fprintf (stderr, "%s: %s", context, (char *)lpMsgBuf); - - LocalFree (lpMsgBuf); - } - - /* We should switch off of last_status, but we'd either return - * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there - * is no CAIRO_STATUS_UNKNOWN_ERROR. - */ - - return CAIRO_STATUS_NO_MEMORY; -} - -void -cairo_set_target_win32 (cairo_t *cr, - HDC hdc) -{ - cairo_surface_t *surface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_win32_surface_create (hdc); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - -static cairo_status_t -_create_dc_and_bitmap (cairo_win32_surface_t *surface, - HDC original_dc, - cairo_format_t format, - int width, - int height, - char **bits_out, - int *rowstride_out) -{ - cairo_status_t status; - - BITMAPINFO *bitmap_info = NULL; - struct { - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[2]; - } bmi_stack; - void *bits; - - int num_palette = 0; /* Quiet GCC */ - int i; - - surface->dc = NULL; - surface->bitmap = NULL; - - switch (format) { - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - num_palette = 0; - break; - - case CAIRO_FORMAT_A8: - num_palette = 256; - break; - - case CAIRO_FORMAT_A1: - num_palette = 2; - break; - } - - if (num_palette > 2) { - bitmap_info = malloc (sizeof (BITMAPINFOHEADER) + num_palette * sizeof (RGBQUAD)); - if (!bitmap_info) - return CAIRO_STATUS_NO_MEMORY; - } else { - bitmap_info = (BITMAPINFO *)&bmi_stack; - } - - bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bitmap_info->bmiHeader.biWidth = width; - bitmap_info->bmiHeader.biHeight = - height; /* top-down */ - bitmap_info->bmiHeader.biSizeImage = 0; - bitmap_info->bmiHeader.biXPelsPerMeter = 72. / 0.0254; /* unused here */ - bitmap_info->bmiHeader.biYPelsPerMeter = 72. / 0.0254; /* unused here */ - bitmap_info->bmiHeader.biPlanes = 1; - - switch (format) { - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - bitmap_info->bmiHeader.biBitCount = 32; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ - bitmap_info->bmiHeader.biClrImportant = 0; - break; - - case CAIRO_FORMAT_A8: - bitmap_info->bmiHeader.biBitCount = 8; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 256; - bitmap_info->bmiHeader.biClrImportant = 0; - - for (i = 0; i < 256; i++) { - bitmap_info->bmiColors[i].rgbBlue = i; - bitmap_info->bmiColors[i].rgbGreen = i; - bitmap_info->bmiColors[i].rgbRed = i; - bitmap_info->bmiColors[i].rgbReserved = 0; - } - - break; - - case CAIRO_FORMAT_A1: - bitmap_info->bmiHeader.biBitCount = 1; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 2; - bitmap_info->bmiHeader.biClrImportant = 0; - - for (i = 0; i < 2; i++) { - bitmap_info->bmiColors[i].rgbBlue = i * 255; - bitmap_info->bmiColors[i].rgbGreen = i * 255; - bitmap_info->bmiColors[i].rgbRed = i * 255; - bitmap_info->bmiColors[i].rgbReserved = 0; - break; - } - } - - surface->dc = CreateCompatibleDC (original_dc); - if (!surface->dc) - goto FAIL; - - surface->bitmap = CreateDIBSection (surface->dc, - bitmap_info, - DIB_RGB_COLORS, - &bits, - NULL, 0); - if (!surface->bitmap) - goto FAIL; - - surface->saved_dc_bitmap = SelectObject (surface->dc, - surface->bitmap); - if (!surface->saved_dc_bitmap) - goto FAIL; - - if (bitmap_info && num_palette > 2) - free (bitmap_info); - - if (bits_out) - *bits_out = bits; - - if (rowstride_out) { - /* Windows bitmaps are padded to 16-bit (word) boundaries */ - switch (format) { - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - *rowstride_out = 4 * width; - break; - - case CAIRO_FORMAT_A8: - *rowstride_out = (width + 1) & -2; - break; - - case CAIRO_FORMAT_A1: - *rowstride_out = ((width + 15) & -16) / 8; - break; - } - } - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); - - if (bitmap_info && num_palette > 2) - free (bitmap_info); - - if (surface->saved_dc_bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - surface->saved_dc_bitmap = NULL; - } - - if (surface->bitmap) { - DeleteObject (surface->bitmap); - surface->bitmap = NULL; - } - - if (surface->dc) { - DeleteDC (surface->dc); - surface->dc = NULL; - } - - return status; -} - -static cairo_surface_t * -_cairo_win32_surface_create_for_dc (HDC original_dc, - cairo_format_t format, - int drawable, - int width, - int height) -{ - cairo_win32_surface_t *surface; - char *bits; - int rowstride; - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (!surface) - return NULL; - - if (_create_dc_and_bitmap (surface, original_dc, format, - width, height, - &bits, &rowstride) != CAIRO_STATUS_SUCCESS) - goto FAIL; - - surface->image = cairo_image_surface_create_for_data (bits, format, - width, height, rowstride); - if (!surface->image) - goto FAIL; - - surface->format = format; - - surface->clip_rect.x = 0; - surface->clip_rect.y = 0; - surface->clip_rect.width = width; - surface->clip_rect.height = height; - - surface->set_clip = 0; - surface->saved_clip = NULL; - - _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); - - return (cairo_surface_t *)surface; - - FAIL: - if (surface->bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - DeleteObject (surface->bitmap); - DeleteDC (surface->dc); - } - if (surface) - free (surface); - - return NULL; - -} - -static cairo_surface_t * -_cairo_win32_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - cairo_win32_surface_t *src = abstract_src; - - return _cairo_win32_surface_create_for_dc (src->dc, format, drawable, - width, height); -} - -/** - * _cairo_win32_surface_create_dib: - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a device-independent-bitmap surface not associated with - * any particular existing surface or device context. The created - * bitmap will be unititialized. - * - * Return value: the newly created surface, or %NULL if it couldn't - * be created (probably because of lack of memory) - **/ -cairo_surface_t * -_cairo_win32_surface_create_dib (cairo_format_t format, - int width, - int height) -{ - return _cairo_win32_surface_create_for_dc (NULL, format, TRUE, - width, height); -} - -static void -_cairo_win32_surface_destroy (void *abstract_surface) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) - cairo_surface_destroy (surface->image); - - if (surface->saved_clip) - DeleteObject (surface->saved_clip); - - /* If we created the Bitmap and DC, destroy them */ - if (surface->bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - DeleteObject (surface->bitmap); - DeleteDC (surface->dc); - } - - free (surface); -} - -static double -_cairo_win32_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We should really get this value from somewhere */ - return 96.0; -} - -static cairo_status_t -_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, - int x, - int y, - int width, - int height, - cairo_win32_surface_t **local_out) -{ - cairo_win32_surface_t *local; - cairo_status_t status; - - local = - (cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface, - surface->format, - 0, - width, height); - if (!local) - return CAIRO_STATUS_NO_MEMORY; - - if (!BitBlt (local->dc, - 0, 0, - width, height, - surface->dc, - x, y, - SRCCOPY)) - goto FAIL; - - *local_out = local; - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_get_subimage"); - - if (local) - cairo_surface_destroy (&local->base); - - return status; -} - -static cairo_status_t -_cairo_win32_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = NULL; - cairo_status_t status; - - if (surface->image) { - *image_out = (cairo_image_surface_t *)surface->image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, - surface->clip_rect.width, - surface->clip_rect.height, &local); - if (CAIRO_OK (status)) { - cairo_surface_set_filter (&local->base, surface->base.filter); - cairo_surface_set_matrix (&local->base, &surface->base.matrix); - cairo_surface_set_repeat (&local->base, surface->base.repeat); - - *image_out = (cairo_image_surface_t *)local->image; - *image_extra = local; - } - - return status; -} - -static void -_cairo_win32_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_win32_surface_t *local = image_extra; - - if (local) - cairo_surface_destroy ((cairo_surface_t *)local); -} - -static cairo_status_t -_cairo_win32_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, - void **image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = NULL; - cairo_status_t status; - RECT clip_box; - int x1, y1, x2, y2; - - if (surface->image) { - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->clip_rect.width; - image_rect->height = surface->clip_rect.height; - - *image_out = (cairo_image_surface_t *)surface->image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; - } - - if (GetClipBox (surface->dc, &clip_box) == ERROR) - return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image"); - - x1 = clip_box.left; - x2 = clip_box.right; - y1 = clip_box.top; - y2 = clip_box.bottom; - - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if (interest_rect->x + interest_rect->width < x2) - x2 = interest_rect->x + interest_rect->width; - if (interest_rect->y + interest_rect->height < y2) - y2 = interest_rect->y + interest_rect->height; - - if (x1 >= x2 || y1 >= y2) { - *image_out = NULL; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_win32_surface_get_subimage (abstract_surface, - x1, y1, x2 - x1, y2 - y1, - &local); - if (CAIRO_OK (status)) { - *image_out = (cairo_image_surface_t *)local->image; - *image_extra = local; - - image_rect->x = x1; - image_rect->y = y1; - image_rect->width = x2 - x1; - image_rect->height = y2 - y1; - } - - return status; -} - -static void -_cairo_win32_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = image_extra; - - if (!local) - return; - - if (!BitBlt (surface->dc, - image_rect->x, image_rect->y, - image_rect->width, image_rect->height, - local->dc, - 0, 0, - SRCCOPY)) - _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); - - cairo_surface_destroy ((cairo_surface_t *)local); -} - -static cairo_status_t -_cairo_win32_surface_clone_similar (void *surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_composite (cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_win32_surface_t *dst = abstract_dst; - cairo_win32_surface_t *src; - cairo_surface_pattern_t *src_surface_pattern; - int alpha; - int integer_transform; - int itx, ity; - - if (pattern->type != CAIRO_PATTERN_SURFACE || - pattern->extend != CAIRO_EXTEND_NONE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (mask_pattern) { - /* FIXME: When we fully support RENDER style 4-channel - * masks we need to check r/g/b != 1.0. - */ - if (mask_pattern->type != CAIRO_PATTERN_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - alpha = (int)(0xffff * pattern->alpha * mask_pattern->alpha) >> 8; - } else { - alpha = (int)(0xffff * pattern->alpha) >> 8; - } - - src_surface_pattern = (cairo_surface_pattern_t *)pattern; - src = (cairo_win32_surface_t *)src_surface_pattern->surface; - - if (src->base.backend != dst->base.backend) - return CAIRO_INT_STATUS_UNSUPPORTED; - - integer_transform = _cairo_matrix_is_integer_translation (&pattern->matrix, &itx, &ity); - if (!integer_transform) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (alpha == 255 && - src->format == dst->format && - (operator == CAIRO_OPERATOR_SRC || - (src->format == CAIRO_FORMAT_RGB24 && operator == CAIRO_OPERATOR_OVER))) { - - if (!BitBlt (dst->dc, - dst_x, dst_y, - width, height, - src->dc, - src_x + itx, src_y + ity, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); - - return CAIRO_STATUS_SUCCESS; - - } else if (integer_transform && - (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) && - dst->format == CAIRO_FORMAT_RGB24 && - !src->base.repeat && - operator == CAIRO_OPERATOR_OVER) { - - BLENDFUNCTION blend_function; - - blend_function.BlendOp = AC_SRC_OVER; - blend_function.BlendFlags = 0; - blend_function.SourceConstantAlpha = alpha; - blend_function.AlphaFormat = src->format == CAIRO_FORMAT_ARGB32 ? AC_SRC_ALPHA : 0; - - if (!AlphaBlend (dst->dc, - dst_x, dst_y, - width, height, - src->dc, - src_x + itx, src_y + ity, - width, height, - blend_function)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - COLORREF new_color; - HBRUSH new_brush; - int i; - - /* If we have a local image, use the fallback code; it will be as fast - * as calling out to GDI. - */ - if (surface->image) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We could support possibly support more operators for color->alpha = 0xffff. - * for CAIRO_OPERATOR_SRC, alpha doesn't matter since we know the destination - * image doesn't have alpha. (surface->pixman_image is non-NULL for all - * surfaces with alpha.) - */ - if (operator != CAIRO_OPERATOR_SRC) - return CAIRO_INT_STATUS_UNSUPPORTED; - - new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); - - new_brush = CreateSolidBrush (new_color); - if (!new_brush) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); - - for (i = 0; i < num_rects; i++) { - RECT rect; - - rect.left = rects[i].x; - rect.top = rects[i].y; - rect.right = rects[i].x + rects[i].width; - rect.bottom = rects[i].y + rects[i].height; - - if (!FillRect (surface->dc, &rect, new_brush)) - goto FAIL; - } - - DeleteObject (new_brush); - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); - - DeleteObject (new_brush); - - return status; -} - -static cairo_int_status_t -_cairo_win32_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) - -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - - /* If we are in-memory, then we set the clip on the image surface - * as well as on the underlying GDI surface. - */ - if (surface->image) - _cairo_surface_set_clip_region (surface->image, region); - - /* The semantics we want is that any clip set by Cairo combines - * is intersected with the clip on device context that the - * surface was created for. To implement this, we need to - * save the original clip when first setting a clip on surface. - */ - - if (region == NULL) { - /* Clear any clip set by Cairo, return to the original */ - - if (surface->set_clip) { - if (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); - - if (surface->saved_clip) { - DeleteObject (surface->saved_clip); - surface->saved_clip = NULL; - } - - surface->set_clip = 0; - } - - - return CAIRO_STATUS_SUCCESS; - - } else { - pixman_box16_t *boxes = pixman_region_rects (region); - int num_boxes = pixman_region_num_rects (region); - pixman_box16_t *extents = pixman_region_extents (region); - RGNDATA *data; - size_t data_size; - RECT *rects; - int i; - HRGN gdi_region; - - /* Create a GDI region for the cairo region */ - - data_size = sizeof (RGNDATAHEADER) + num_boxes * sizeof (RECT); - data = malloc (data_size); - if (!data) - return CAIRO_STATUS_NO_MEMORY; - rects = (RECT *)data->Buffer; - - data->rdh.dwSize = sizeof (RGNDATAHEADER); - data->rdh.iType = RDH_RECTANGLES; - data->rdh.nCount = num_boxes; - data->rdh.nRgnSize = num_boxes * sizeof (RECT); - data->rdh.rcBound.left = extents->x1; - data->rdh.rcBound.top = extents->y1; - data->rdh.rcBound.right = extents->x2; - data->rdh.rcBound.bottom = extents->y2; - - for (i = 0; i < num_boxes; i++) { - rects[i].left = boxes[i].x1; - rects[i].top = boxes[i].y1; - rects[i].right = boxes[i].x2; - rects[i].bottom = boxes[i].y2; - } - - gdi_region = ExtCreateRegion (NULL, data_size, data); - free (data); - - if (!gdi_region) - return CAIRO_STATUS_NO_MEMORY; - - if (surface->set_clip) { - /* Combine the new region with the original clip */ - - if (surface->saved_clip) { - if (CombineRgn (gdi_region, gdi_region, surface->saved_clip, RGN_AND) == ERROR) - goto FAIL; - } - - if (SelectClipRgn (surface->dc, gdi_region) == ERROR) - goto FAIL; - - } else { - /* Save the the current region */ - - surface->saved_clip = CreateRectRgn (0, 0, 0, 0); - if (!surface->saved_clip) { - goto FAIL; } - - /* This function has no error return! */ - if (GetClipRgn (surface->dc, surface->saved_clip) == 0) { /* No clip */ - DeleteObject (surface->saved_clip); - surface->saved_clip = NULL; - } - - if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) - goto FAIL; - - surface->set_clip = 1; - } - - DeleteObject (gdi_region); - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); - DeleteObject (gdi_region); - return status; - } -} - -static cairo_status_t -_cairo_win32_surface_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -cairo_surface_t * -cairo_win32_surface_create (HDC hdc) -{ - cairo_win32_surface_t *surface; - RECT rect; - - /* Try to figure out the drawing bounds for the Device context - */ - if (GetClipBox (hdc, &rect) == ERROR) { - _cairo_win32_print_gdi_error ("cairo_win32_surface_create"); - return NULL; - } - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (!surface) - return NULL; - - surface->image = NULL; - surface->format = CAIRO_FORMAT_RGB24; - - surface->dc = hdc; - surface->bitmap = NULL; - - surface->clip_rect.x = rect.left; - surface->clip_rect.y = rect.top; - surface->clip_rect.width = rect.right - rect.left; - surface->clip_rect.height = rect.bottom - rect.top; - - surface->set_clip = 0; - surface->saved_clip = NULL; - - _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); - - return (cairo_surface_t *)surface; -} - -/** - * _cairo_surface_is_win32: - * @surface: a #cairo_surface_t - * - * Checks if a surface is an #cairo_win32_surface_t - * - * Return value: True if the surface is an win32 surface - **/ -int -_cairo_surface_is_win32 (cairo_surface_t *surface) -{ - return surface->backend == &cairo_win32_surface_backend; -} - -static const cairo_surface_backend_t cairo_win32_surface_backend = { - _cairo_win32_surface_create_similar, - _cairo_win32_surface_destroy, - _cairo_win32_surface_pixels_per_inch, - _cairo_win32_surface_acquire_source_image, - _cairo_win32_surface_release_source_image, - _cairo_win32_surface_acquire_dest_image, - _cairo_win32_surface_release_dest_image, - _cairo_win32_surface_clone_similar, - _cairo_win32_surface_composite, - _cairo_win32_surface_fill_rectangles, - _cairo_win32_surface_composite_trapezoids, - _cairo_win32_surface_copy_page, - _cairo_win32_surface_show_page, - _cairo_win32_surface_set_clip_region, - _cairo_win32_surface_show_glyphs -}; diff --git a/src/cairo_xcb_surface.c b/src/cairo_xcb_surface.c deleted file mode 100644 index 0694b77a2..000000000 --- a/src/cairo_xcb_surface.c +++ /dev/null @@ -1,978 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" -#include "cairo-xcb.h" - -cairo_surface_t * -cairo_xcb_surface_create (XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format); - -#define AllPlanes ((unsigned long)~0L) - -static XCBRenderPICTFORMAT -format_from_visual(XCBConnection *c, XCBVISUALID visual) -{ - static const XCBRenderPICTFORMAT nil = { 0 }; - XCBRenderQueryPictFormatsRep *r; - XCBRenderPICTSCREENIter si; - XCBRenderPICTDEPTHIter di; - XCBRenderPICTVISUALIter vi; - - r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); - if(!r) - return nil; - - for(si = XCBRenderQueryPictFormatsScreensIter(r); si.rem; XCBRenderPICTSCREENNext(&si)) - for(di = XCBRenderPICTSCREENDepthsIter(si.data); di.rem; XCBRenderPICTDEPTHNext(&di)) - for(vi = XCBRenderPICTDEPTHVisualsIter(di.data); vi.rem; XCBRenderPICTVISUALNext(&vi)) - if(vi.data->visual.id == visual.id) - { - XCBRenderPICTFORMAT ret = vi.data->format; - free(r); - return ret; - } - - return nil; -} - -static XCBRenderPICTFORMAT -format_from_cairo(XCBConnection *c, cairo_format_t fmt) -{ - XCBRenderPICTFORMAT ret = { 0 }; - struct tmpl_t { - XCBRenderDIRECTFORMAT direct; - CARD8 depth; - }; - static const struct tmpl_t templates[] = { - /* CAIRO_FORMAT_ARGB32 */ - { - { - 16, 0xff, - 8, 0xff, - 0, 0xff, - 24, 0xff - }, - 32 - }, - /* CAIRO_FORMAT_RGB24 */ - { - { - 16, 0xff, - 8, 0xff, - 0, 0xff, - 0, 0x00 - }, - 24 - }, - /* CAIRO_FORMAT_A8 */ - { - { - 0, 0x00, - 0, 0x00, - 0, 0x00, - 0, 0xff - }, - 8 - }, - /* CAIRO_FORMAT_A1 */ - { - { - 0, 0x00, - 0, 0x00, - 0, 0x00, - 0, 0x01 - }, - 1 - }, - }; - const struct tmpl_t *tmpl; - XCBRenderQueryPictFormatsRep *r; - XCBRenderPICTFORMINFOIter fi; - - if(fmt < 0 || fmt >= (sizeof(templates) / sizeof(*templates))) - return ret; - tmpl = templates + fmt; - - r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); - if(!r) - return ret; - - for(fi = XCBRenderQueryPictFormatsFormatsIter(r); fi.rem; XCBRenderPICTFORMINFONext(&fi)) - { - const XCBRenderDIRECTFORMAT *t, *f; - if(fi.data->type != XCBRenderPictTypeDirect) - continue; - if(fi.data->depth != tmpl->depth) - continue; - t = &tmpl->direct; - f = &fi.data->direct; - if(t->red_mask && (t->red_mask != f->red_mask || t->red_shift != f->red_shift)) - continue; - if(t->green_mask && (t->green_mask != f->green_mask || t->green_shift != f->green_shift)) - continue; - if(t->blue_mask && (t->blue_mask != f->blue_mask || t->blue_shift != f->blue_shift)) - continue; - if(t->alpha_mask && (t->alpha_mask != f->alpha_mask || t->alpha_shift != f->alpha_shift)) - continue; - - ret = fi.data->id; - } - - free(r); - return ret; -} - -void -cairo_set_target_xcb (cairo_t *cr, - XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format) -{ - cairo_surface_t *surface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_xcb_surface_create (dpy, drawable, visual, format); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - -typedef struct cairo_xcb_surface { - cairo_surface_t base; - - XCBConnection *dpy; - XCBGCONTEXT gc; - XCBDRAWABLE drawable; - int owns_pixmap; - XCBVISUALTYPE *visual; - cairo_format_t format; - - int render_major; - int render_minor; - - int width; - int height; - - XCBRenderPICTURE picture; -} cairo_xcb_surface_t; - -#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ - (((surface)->render_major > major) || \ - (((surface)->render_major == major) && ((surface)->render_minor >= minor))) - -#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) - -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) - -#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) -#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) - -#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) - -#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) - -static int -_CAIRO_FORMAT_DEPTH (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_A1: - return 1; - case CAIRO_FORMAT_A8: - return 8; - case CAIRO_FORMAT_RGB24: - return 24; - case CAIRO_FORMAT_ARGB32: - default: - return 32; - } -} - -static cairo_surface_t * -_cairo_xcb_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - cairo_xcb_surface_t *src = abstract_src; - XCBConnection *dpy = src->dpy; - XCBDRAWABLE d; - cairo_xcb_surface_t *surface; - - /* XXX: There's a pretty lame heuristic here. This assumes that - * all non-Render X servers do not support depth-32 pixmaps, (and - * that they do support depths 1, 8, and 24). Obviously, it would - * be much better to check the depths that are actually - * supported. */ - if (!dpy - || (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src) - && format == CAIRO_FORMAT_ARGB32)) - { - return NULL; - } - - d.pixmap = XCBPIXMAPNew (dpy); - XCBCreatePixmap (dpy, _CAIRO_FORMAT_DEPTH (format), - d.pixmap, src->drawable, - width, height); - - surface = (cairo_xcb_surface_t *) - cairo_xcb_surface_create (dpy, d, NULL, format); - surface->owns_pixmap = 1; - - surface->width = width; - surface->height = height; - - return &surface->base; -} - -static void -_cairo_xcb_surface_destroy (void *abstract_surface) -{ - cairo_xcb_surface_t *surface = abstract_surface; - if (surface->picture.xid) - XCBRenderFreePicture (surface->dpy, surface->picture); - - if (surface->owns_pixmap) - XCBFreePixmap (surface->dpy, surface->drawable.pixmap); - - if (surface->gc.xid) - XCBFreeGC (surface->dpy, surface->gc); - - surface->dpy = 0; - - free (surface); -} - -static double -_cairo_xcb_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We should really get this value from somewhere like Xft.dpy */ - return 96.0; -} - -static int -bits_per_pixel(XCBConnection *c, int depth) -{ - XCBFORMAT *fmt = XCBConnSetupSuccessRepPixmapFormats(XCBGetSetup(c)); - XCBFORMAT *fmtend = fmt + XCBConnSetupSuccessRepPixmapFormatsLength(XCBGetSetup(c)); - - for(; fmt != fmtend; ++fmt) - if(fmt->depth == depth) - return fmt->bits_per_pixel; - - if(depth <= 4) - return 4; - if(depth <= 8) - return 8; - if(depth <= 16) - return 16; - return 32; -} - -static int -bytes_per_line(XCBConnection *c, int width, int bpp) -{ - int bitmap_pad = XCBGetSetup(c)->bitmap_format_scanline_pad; - return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3; -} - -static cairo_status_t -_get_image_surface (cairo_xcb_surface_t *surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect) -{ - cairo_image_surface_t *image; - XCBGetGeometryRep *geomrep; - XCBGetImageRep *imagerep; - int bpp; - int x1, y1, x2, y2; - - geomrep = XCBGetGeometryReply(surface->dpy, XCBGetGeometry(surface->dpy, surface->drawable), 0); - if(!geomrep) - return 0; - - surface->width = geomrep->width; - surface->height = geomrep->height; - free(geomrep); - - x1 = 0; - y1 = 0; - x2 = surface->width; - y2 = surface->height; - - if (interest_rect) { - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if (interest_rect->x + interest_rect->width < x2) - x2 = interest_rect->x + interest_rect->width; - if (interest_rect->y + interest_rect->height < y2) - y2 = interest_rect->y + interest_rect->height; - - if (x1 >= x2 || y1 >= y2) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - if (image_rect) { - image_rect->x = x1; - image_rect->y = y1; - image_rect->width = x2 - x1; - image_rect->height = y2 - y1; - } - - imagerep = XCBGetImageReply(surface->dpy, - XCBGetImage(surface->dpy, ZPixmap, - surface->drawable, - x1, y1, - x2 - x1, y2 - y1, - AllPlanes), 0); - if(!imagerep) - return 0; - - bpp = bits_per_pixel(surface->dpy, imagerep->depth); - - if (surface->visual) { - cairo_format_masks_t masks; - - /* XXX: Add support here for pictures with external alpha? */ - - masks.bpp = bpp; - masks.alpha_mask = 0; - masks.red_mask = surface->visual->red_mask; - masks.green_mask = surface->visual->green_mask; - masks.blue_mask = surface->visual->blue_mask; - - image = _cairo_image_surface_create_with_masks (XCBGetImageData(imagerep), - &masks, - x2 - x1, - y2 - y1, - bytes_per_line(surface->dpy, surface->width, bpp)); - } else { - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (XCBGetImageData(imagerep), - surface->format, - x2 - x1, - y2 - y1, - bytes_per_line(surface->dpy, surface->width, bpp)); - } - - /* Let the surface take ownership of the data */ - /* XXX: Can probably come up with a cleaner API here. */ - _cairo_image_surface_assume_ownership_of_data (image); - /* FIXME: imagerep can't be freed correctly, I think. must copy. :-( */ - - _cairo_image_surface_set_repeat (image, surface->base.repeat); - _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - - *image_out = image; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) -{ - if (surface->gc.xid) - return; - - surface->gc = XCBGCONTEXTNew(surface->dpy); - XCBCreateGC (surface->dpy, surface->gc, surface->drawable, 0, 0); -} - -static cairo_status_t -_draw_image_surface (cairo_xcb_surface_t *surface, - cairo_image_surface_t *image, - int dst_x, - int dst_y) -{ - int bpp, data_len; - - _cairo_xcb_surface_ensure_gc (surface); - bpp = bits_per_pixel(surface->dpy, image->depth); - data_len = bytes_per_line(surface->dpy, image->width, bpp) * image->height; - XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, - image->width, - image->height, - dst_x, dst_y, - /* left_pad */ 0, image->depth, - data_len, image->data); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xcb_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, NULL, &image, NULL); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_set_filter (&image->base, surface->base.filter); - cairo_surface_set_matrix (&image->base, &surface->base.matrix); - cairo_surface_set_repeat (&image->base, surface->base.repeat); - - *image_out = image; - } - - return status; -} - -static void -_cairo_xcb_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_xcb_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect_out, - void **image_extra) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, interest_rect, &image, image_rect_out); - if (status == CAIRO_STATUS_SUCCESS) - *image_out = image; - - return status; -} - -static void -_cairo_xcb_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ - cairo_xcb_surface_t *surface = abstract_surface; - - /* ignore errors */ - _draw_image_surface (surface, image, image_rect->x, image_rect->y); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_xcb_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - cairo_xcb_surface_t *surface = abstract_surface; - cairo_xcb_surface_t *clone; - - if (src->backend == surface->base.backend ) { - cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src; - - if (xcb_src->dpy == surface->dpy) { - *clone_out = src; - cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - - clone = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_similar (surface, image_src->format, 0, - image_src->width, image_src->height); - if (clone == NULL) - return CAIRO_STATUS_NO_MEMORY; - - _draw_image_surface (clone, image_src, 0, 0); - - *clone_out = &clone->base; - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, - cairo_matrix_t *matrix) -{ - XCBRenderTRANSFORM xtransform; - - if (!surface->picture.xid) - return CAIRO_STATUS_SUCCESS; - - xtransform.matrix11 = _cairo_fixed_from_double (matrix->m[0][0]); - xtransform.matrix12 = _cairo_fixed_from_double (matrix->m[1][0]); - xtransform.matrix13 = _cairo_fixed_from_double (matrix->m[2][0]); - - xtransform.matrix21 = _cairo_fixed_from_double (matrix->m[0][1]); - xtransform.matrix22 = _cairo_fixed_from_double (matrix->m[1][1]); - xtransform.matrix23 = _cairo_fixed_from_double (matrix->m[2][1]); - - xtransform.matrix31 = 0; - xtransform.matrix32 = 0; - xtransform.matrix33 = _cairo_fixed_from_double (1); - - if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - { - static const XCBRenderTRANSFORM identity = { - 1 << 16, 0x00000, 0x00000, - 0x00000, 1 << 16, 0x00000, - 0x00000, 0x00000, 1 << 16 - }; - - if (memcmp (&xtransform, &identity, sizeof (XCBRenderTRANSFORM)) == 0) - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, - cairo_filter_t filter) -{ - char *render_filter; - - if (!surface->picture.xid) - return CAIRO_STATUS_SUCCESS; - - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) - { - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - switch (filter) { - case CAIRO_FILTER_FAST: - render_filter = "fast"; - break; - case CAIRO_FILTER_GOOD: - render_filter = "good"; - break; - case CAIRO_FILTER_BEST: - render_filter = "best"; - break; - case CAIRO_FILTER_NEAREST: - render_filter = "nearest"; - break; - case CAIRO_FILTER_BILINEAR: - render_filter = "bilinear"; - break; - default: - render_filter = "best"; - break; - } - - XCBRenderSetPictureFilter(surface->dpy, surface->picture, - strlen(render_filter), render_filter, 0, NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, int repeat) -{ - CARD32 mask = XCBRenderCPRepeat; - CARD32 pa[] = { repeat }; - - if (!surface->picture.xid) - return CAIRO_STATUS_SUCCESS; - - XCBRenderChangePicture (surface->dpy, surface->picture, mask, pa); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, - cairo_surface_attributes_t *attributes) -{ - cairo_int_status_t status; - - status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix); - if (status) - return status; - - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - _cairo_xcb_surface_set_repeat (surface, 0); - break; - case CAIRO_EXTEND_REPEAT: - _cairo_xcb_surface_set_repeat (surface, 1); - break; - case CAIRO_EXTEND_REFLECT: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _cairo_xcb_surface_set_filter (surface, attributes->filter); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static int -_render_operator (cairo_operator_t operator) -{ - switch (operator) { - case CAIRO_OPERATOR_CLEAR: - return XCBRenderPictOpClear; - case CAIRO_OPERATOR_SRC: - return XCBRenderPictOpSrc; - case CAIRO_OPERATOR_DST: - return XCBRenderPictOpDst; - case CAIRO_OPERATOR_OVER: - return XCBRenderPictOpOver; - case CAIRO_OPERATOR_OVER_REVERSE: - return XCBRenderPictOpOverReverse; - case CAIRO_OPERATOR_IN: - return XCBRenderPictOpIn; - case CAIRO_OPERATOR_IN_REVERSE: - return XCBRenderPictOpInReverse; - case CAIRO_OPERATOR_OUT: - return XCBRenderPictOpOut; - case CAIRO_OPERATOR_OUT_REVERSE: - return XCBRenderPictOpOutReverse; - case CAIRO_OPERATOR_ATOP: - return XCBRenderPictOpAtop; - case CAIRO_OPERATOR_ATOP_REVERSE: - return XCBRenderPictOpAtopReverse; - case CAIRO_OPERATOR_XOR: - return XCBRenderPictOpXor; - case CAIRO_OPERATOR_ADD: - return XCBRenderPictOpAdd; - case CAIRO_OPERATOR_SATURATE: - return XCBRenderPictOpSaturate; - default: - return XCBRenderPictOpOver; - } -} - -static cairo_int_status_t -_cairo_xcb_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_surface_attributes_t src_attr, mask_attr; - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src; - cairo_xcb_surface_t *mask; - cairo_int_status_t status; - - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - (cairo_surface_t **) &src, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); - if (status) - return status; - - status = _cairo_xcb_surface_set_attributes (src, &src_attr); - if (CAIRO_OK (status)) - { - if (mask) - { - status = _cairo_xcb_surface_set_attributes (mask, &mask_attr); - if (CAIRO_OK (status)) - XCBRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - mask->picture, - dst->picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } - else - { - static XCBRenderPICTURE maskpict = { 0 }; - - XCBRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - maskpict, - dst->picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - } - - if (mask) - _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - - return status; -} - -static cairo_int_status_t -_cairo_xcb_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_xcb_surface_t *surface = abstract_surface; - XCBRenderCOLOR render_color; - - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - render_color.red = color->red_short; - render_color.green = color->green_short; - render_color.blue = color->blue_short; - render_color.alpha = color->alpha_short; - - /* XXX: This XCBRECTANGLE cast is evil... it needs to go away somehow. */ - XCBRenderFillRectangles (surface->dpy, - _render_operator (operator), - surface->picture, - render_color, num_rects, (XCBRECTANGLE *) rects); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - cairo_surface_attributes_t attributes; - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src; - cairo_int_status_t status; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - - if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - src_x, src_y, width, height, - (cairo_surface_t **) &src, - &attributes); - if (status) - return status; - - if (traps[0].left.p1.y < traps[0].left.p2.y) { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ - /* XXX: format_from_cairo is slow. should cache something. */ - status = _cairo_xcb_surface_set_attributes (src, &attributes); - if (CAIRO_OK (status)) - XCBRenderTrapezoids (dst->dpy, - _render_operator (operator), - src->picture, dst->picture, - format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - num_traps, (XCBRenderTRAP *) traps); - - _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); - - return status; -} - -static cairo_int_status_t -_cairo_xcb_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_xcb_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_xcb_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - /* FIXME */ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static const cairo_surface_backend_t cairo_xcb_surface_backend = { - _cairo_xcb_surface_create_similar, - _cairo_xcb_surface_destroy, - _cairo_xcb_surface_pixels_per_inch, - _cairo_xcb_surface_acquire_source_image, - _cairo_xcb_surface_release_source_image, - _cairo_xcb_surface_acquire_dest_image, - _cairo_xcb_surface_release_dest_image, - _cairo_xcb_surface_clone_similar, - _cairo_xcb_surface_composite, - _cairo_xcb_surface_fill_rectangles, - _cairo_xcb_surface_composite_trapezoids, - _cairo_xcb_surface_copy_page, - _cairo_xcb_surface_show_page, - _cairo_xcb_surface_set_clip_region, - NULL /* show_glyphs */ -}; - -static void -query_render_version (XCBConnection *c, cairo_xcb_surface_t *surface) -{ - XCBRenderQueryVersionRep *r; - - surface->render_major = -1; - surface->render_minor = -1; - - if (!XCBRenderInit(c)) - return; - - r = XCBRenderQueryVersionReply(c, XCBRenderQueryVersion(c, 0, 6), 0); - if (!r) - return; - - surface->render_major = r->major_version; - surface->render_minor = r->minor_version; - free(r); -} - -cairo_surface_t * -cairo_xcb_surface_create (XCBConnection *dpy, - XCBDRAWABLE drawable, - XCBVISUALTYPE *visual, - cairo_format_t format) -{ - cairo_xcb_surface_t *surface; - - surface = malloc (sizeof (cairo_xcb_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init (&surface->base, &cairo_xcb_surface_backend); - - surface->dpy = dpy; - surface->gc.xid = 0; - surface->drawable = drawable; - surface->owns_pixmap = 0; - surface->visual = visual; - surface->format = format; - - query_render_version(dpy, surface); - - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) - { - XCBRenderPICTFORMAT fmt; - if(visual) - fmt = format_from_visual (dpy, visual->visual_id); - else - fmt = format_from_cairo (dpy, format); - surface->picture = XCBRenderPICTURENew(dpy); - XCBRenderCreatePicture (dpy, surface->picture, drawable, - fmt, 0, NULL); - } - else - surface->picture.xid = 0; - - return (cairo_surface_t *) surface; -} diff --git a/src/cairo_xlib_surface.c b/src/cairo_xlib_surface.c deleted file mode 100644 index 3eaef57e5..000000000 --- a/src/cairo_xlib_surface.c +++ /dev/null @@ -1,1530 +0,0 @@ -/* 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. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is University of Southern - * California. - * - * Contributor(s): - * Carl D. Worth <cworth@cworth.org> - */ - -#include "cairoint.h" -#include "cairo-xlib.h" - -/** - * cairo_set_target_drawable: - * @cr: a #cairo_t - * @dpy: an X display - * @drawable: a window or pixmap on the default screen of @dpy - * - * Directs output for a #cairo_t to an Xlib drawable. @drawable must - * be a Window or Pixmap on the default screen of @dpy using the - * default colormap and visual. Using this function is slow because - * the function must retrieve information about @drawable from the X - * server. - - * The combination of cairo_xlib_surface_create() and - * cairo_set_target_surface() is somewhat more flexible, although - * it still is slow. - **/ -void -cairo_set_target_drawable (cairo_t *cr, - Display *dpy, - Drawable drawable) -{ - cairo_surface_t *surface; - - if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) - return; - - surface = cairo_xlib_surface_create (dpy, drawable, - DefaultVisual (dpy, DefaultScreen (dpy)), - 0, - DefaultColormap (dpy, DefaultScreen (dpy))); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - -typedef struct _cairo_xlib_surface { - cairo_surface_t base; - - Display *dpy; - GC gc; - Drawable drawable; - int owns_pixmap; - Visual *visual; - cairo_format_t format; - - int render_major; - int render_minor; - - int width; - int height; - - Picture picture; -} cairo_xlib_surface_t; - -#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ - (((surface)->render_major > major) || \ - (((surface)->render_major == major) && ((surface)->render_minor >= minor))) - -#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) - - -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) - -#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) -#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) - -#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) - -#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) - -static int -_CAIRO_FORMAT_DEPTH (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_A1: - return 1; - case CAIRO_FORMAT_A8: - return 8; - case CAIRO_FORMAT_RGB24: - return 24; - case CAIRO_FORMAT_ARGB32: - default: - return 32; - } -} - -static cairo_surface_t * -_cairo_xlib_surface_create_with_size (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap, - int width, - int height); - - -static cairo_surface_t * -_cairo_xlib_surface_create_similar (void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - cairo_xlib_surface_t *src = abstract_src; - Display *dpy = src->dpy; - int scr; - Pixmap pix; - cairo_xlib_surface_t *surface; - - /* As a good first approximation, if the display doesn't have COMPOSITE, - * we're better off using image surfaces for all temporary operations - */ - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE(src)) { - return cairo_image_surface_create (format, width, height); - } - - scr = DefaultScreen (dpy); - - pix = XCreatePixmap (dpy, DefaultRootWindow (dpy), - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - _CAIRO_FORMAT_DEPTH (format)); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_with_size (dpy, pix, NULL, format, - DefaultColormap (dpy, scr), - width, height); - surface->owns_pixmap = 1; - - surface->width = width; - surface->height = height; - - return &surface->base; -} - -static void -_cairo_xlib_surface_destroy (void *abstract_surface) -{ - cairo_xlib_surface_t *surface = abstract_surface; - if (surface->picture) - XRenderFreePicture (surface->dpy, surface->picture); - - if (surface->owns_pixmap) - XFreePixmap (surface->dpy, surface->drawable); - - if (surface->gc) - XFreeGC (surface->dpy, surface->gc); - - surface->dpy = 0; - - free (surface); -} - -static double -_cairo_xlib_surface_pixels_per_inch (void *abstract_surface) -{ - /* XXX: We should really get this value from somewhere like Xft.dpy */ - return 96.0; -} - -static cairo_status_t -_get_image_surface (cairo_xlib_surface_t *surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect) -{ - cairo_image_surface_t *image; - XImage *ximage; - Window root_ignore; - int x_ignore, y_ignore, bwidth_ignore, depth_ignore; - int x1, y1, x2, y2; - - XGetGeometry (surface->dpy, - surface->drawable, - &root_ignore, &x_ignore, &y_ignore, - &surface->width, &surface->height, - &bwidth_ignore, &depth_ignore); - - x1 = 0; - y1 = 0; - x2 = surface->width; - y2 = surface->height; - - if (interest_rect) { - if (interest_rect->x > x1) - x1 = interest_rect->x; - if (interest_rect->y > y1) - y1 = interest_rect->y; - if (interest_rect->x + interest_rect->width < x2) - x2 = interest_rect->x + interest_rect->width; - if (interest_rect->y + interest_rect->height < y2) - y2 = interest_rect->y + interest_rect->height; - - if (x1 >= x2 || y1 >= y2) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - if (image_rect) { - image_rect->x = x1; - image_rect->y = y1; - image_rect->width = x2 - x1; - image_rect->height = y2 - y1; - } - - /* XXX: This should try to use the XShm extension if availible */ - ximage = XGetImage (surface->dpy, - surface->drawable, - x1, y1, - x2 - x1, y2 - y1, - AllPlanes, ZPixmap); - - if (surface->visual) { - cairo_format_masks_t masks; - - /* XXX: Add support here for pictures with external alpha? */ - - masks.bpp = ximage->bits_per_pixel; - masks.alpha_mask = 0; - masks.red_mask = surface->visual->red_mask; - masks.green_mask = surface->visual->green_mask; - masks.blue_mask = surface->visual->blue_mask; - - image = _cairo_image_surface_create_with_masks (ximage->data, - &masks, - ximage->width, - ximage->height, - ximage->bytes_per_line); - } else { - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (ximage->data, - surface->format, - ximage->width, - ximage->height, - ximage->bytes_per_line); - } - - /* Let the surface take ownership of the data */ - /* XXX: Can probably come up with a cleaner API here. */ - _cairo_image_surface_assume_ownership_of_data (image); - ximage->data = NULL; - XDestroyImage (ximage); - - _cairo_image_surface_set_repeat (image, surface->base.repeat); - _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - - *image_out = image; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) -{ - if (surface->gc) - return; - - surface->gc = XCreateGC (surface->dpy, surface->drawable, 0, NULL); -} - -static cairo_status_t -_draw_image_surface (cairo_xlib_surface_t *surface, - cairo_image_surface_t *image, - int dst_x, - int dst_y) -{ - XImage *ximage; - unsigned bitmap_pad; - - if (image->depth > 16) - bitmap_pad = 32; - else if (image->depth > 8) - bitmap_pad = 16; - else - bitmap_pad = 8; - - ximage = XCreateImage (surface->dpy, - DefaultVisual(surface->dpy, DefaultScreen(surface->dpy)), - image->depth, - ZPixmap, - 0, - image->data, - image->width, - image->height, - bitmap_pad, - image->stride); - if (ximage == NULL) - return CAIRO_STATUS_NO_MEMORY; - - _cairo_xlib_surface_ensure_gc (surface); - XPutImage(surface->dpy, surface->drawable, surface->gc, - ximage, 0, 0, dst_x, dst_y, - image->width, image->height); - - /* Foolish XDestroyImage thinks it can free my data, but I won't - stand for it. */ - ximage->data = NULL; - XDestroyImage (ximage); - - return CAIRO_STATUS_SUCCESS; - -} - -static cairo_status_t -_cairo_xlib_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, NULL, &image, NULL); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_set_filter (&image->base, surface->base.filter); - cairo_surface_set_matrix (&image->base, &surface->base.matrix); - cairo_surface_set_repeat (&image->base, surface->base.repeat); - - *image_out = image; - } - - return status; -} - -static void -_cairo_xlib_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect_out, - void **image_extra) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _get_image_surface (surface, interest_rect, &image, image_rect_out); - if (status == CAIRO_STATUS_SUCCESS) - *image_out = image; - - return status; -} - -static void -_cairo_xlib_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, - void *image_extra) -{ - cairo_xlib_surface_t *surface = abstract_surface; - - /* ignore errors */ - _draw_image_surface (surface, image, image_rect->x, image_rect->y); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_xlib_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_surface_t **clone_out) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_xlib_surface_t *clone; - - if (src->backend == surface->base.backend ) { - cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; - - if (xlib_src->dpy == surface->dpy) { - *clone_out = src; - cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - - clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (surface, image_src->format, 0, - image_src->width, image_src->height); - if (clone == NULL) - return CAIRO_STATUS_NO_MEMORY; - - _draw_image_surface (clone, image_src, 0, 0); - - *clone_out = &clone->base; - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, - cairo_matrix_t *matrix) -{ - XTransform xtransform; - - if (!surface->picture) - return CAIRO_STATUS_SUCCESS; - - xtransform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - xtransform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - xtransform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - - xtransform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - xtransform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - xtransform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - - xtransform.matrix[2][0] = 0; - xtransform.matrix[2][1] = 0; - xtransform.matrix[2][2] = _cairo_fixed_from_double (1); - - if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - { - static const XTransform identity = { { - { 1 << 16, 0x00000, 0x00000 }, - { 0x00000, 1 << 16, 0x00000 }, - { 0x00000, 0x00000, 1 << 16 }, - } }; - - if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0) - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - XRenderSetPictureTransform (surface->dpy, surface->picture, &xtransform); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, - cairo_filter_t filter) -{ - char *render_filter; - - if (!surface->picture) - return CAIRO_STATUS_SUCCESS; - - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) - { - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - return CAIRO_STATUS_SUCCESS; - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - switch (filter) { - case CAIRO_FILTER_FAST: - render_filter = FilterFast; - break; - case CAIRO_FILTER_GOOD: - render_filter = FilterGood; - break; - case CAIRO_FILTER_BEST: - render_filter = FilterBest; - break; - case CAIRO_FILTER_NEAREST: - render_filter = FilterNearest; - break; - case CAIRO_FILTER_BILINEAR: - render_filter = FilterBilinear; - break; - default: - render_filter = FilterBest; - break; - } - - XRenderSetPictureFilter (surface->dpy, surface->picture, - render_filter, NULL, 0); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) -{ - XRenderPictureAttributes pa; - unsigned long mask; - - if (!surface->picture) - return CAIRO_STATUS_SUCCESS; - - mask = CPRepeat; - pa.repeat = repeat; - - XRenderChangePicture (surface->dpy, surface->picture, mask, &pa); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, - cairo_surface_attributes_t *attributes) -{ - cairo_int_status_t status; - - status = _cairo_xlib_surface_set_matrix (surface, &attributes->matrix); - if (status) - return status; - - switch (attributes->extend) { - case CAIRO_EXTEND_NONE: - _cairo_xlib_surface_set_repeat (surface, 0); - break; - case CAIRO_EXTEND_REPEAT: - _cairo_xlib_surface_set_repeat (surface, 1); - break; - case CAIRO_EXTEND_REFLECT: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _cairo_xlib_surface_set_filter (surface, attributes->filter); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static int -_render_operator (cairo_operator_t operator) -{ - switch (operator) { - case CAIRO_OPERATOR_CLEAR: - return PictOpClear; - case CAIRO_OPERATOR_SRC: - return PictOpSrc; - case CAIRO_OPERATOR_DST: - return PictOpDst; - case CAIRO_OPERATOR_OVER: - return PictOpOver; - case CAIRO_OPERATOR_OVER_REVERSE: - return PictOpOverReverse; - case CAIRO_OPERATOR_IN: - return PictOpIn; - case CAIRO_OPERATOR_IN_REVERSE: - return PictOpInReverse; - case CAIRO_OPERATOR_OUT: - return PictOpOut; - case CAIRO_OPERATOR_OUT_REVERSE: - return PictOpOutReverse; - case CAIRO_OPERATOR_ATOP: - return PictOpAtop; - case CAIRO_OPERATOR_ATOP_REVERSE: - return PictOpAtopReverse; - case CAIRO_OPERATOR_XOR: - return PictOpXor; - case CAIRO_OPERATOR_ADD: - return PictOpAdd; - case CAIRO_OPERATOR_SATURATE: - return PictOpSaturate; - default: - return PictOpOver; - } -} - -static cairo_int_status_t -_cairo_xlib_surface_composite (cairo_operator_t operator, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_surface_attributes_t src_attr, mask_attr; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_surface_t *mask; - cairo_int_status_t status; - - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - (cairo_surface_t **) &src, - (cairo_surface_t **) &mask, - &src_attr, &mask_attr); - if (status) - return status; - - status = _cairo_xlib_surface_set_attributes (src, &src_attr); - if (CAIRO_OK (status)) - { - if (mask) - { - status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); - if (CAIRO_OK (status)) - XRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - mask->picture, - dst->picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } - else - { - XRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - 0, - dst->picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - } - - if (mask) - _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - - return status; -} - -static cairo_int_status_t -_cairo_xlib_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t operator, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_xlib_surface_t *surface = abstract_surface; - XRenderColor render_color; - - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - render_color.red = color->red_short; - render_color.green = color->green_short; - render_color.blue = color->blue_short; - render_color.alpha = color->alpha_short; - - /* XXX: This XRectangle cast is evil... it needs to go away somehow. */ - XRenderFillRectangles (surface->dpy, - _render_operator (operator), - surface->picture, - &render_color, (XRectangle *) rects, num_rects); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps) -{ - cairo_surface_attributes_t attributes; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_int_status_t status; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - - if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - src_x, src_y, width, height, - (cairo_surface_t **) &src, - &attributes); - if (status) - return status; - - if (traps[0].left.p1.y < traps[0].left.p2.y) { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); - render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ - status = _cairo_xlib_surface_set_attributes (src, &attributes); - if (CAIRO_OK (status)) - XRenderCompositeTrapezoids (dst->dpy, - _render_operator (operator), - src->picture, dst->picture, - XRenderFindStandardFormat (dst->dpy, PictStandardA8), - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - (XTrapezoid *) traps, num_traps); - - _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); - - return status; -} - -static cairo_int_status_t -_cairo_xlib_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_xlib_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_xlib_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - - Region xregion; - XRectangle xr; - XRectangle *rects = NULL; - XGCValues gc_values; - pixman_box16_t *box; - cairo_xlib_surface_t *surf; - int n, m; - - surf = (cairo_xlib_surface_t *) abstract_surface; - - if (region == NULL) { - /* NULL region == reset the clip */ - xregion = XCreateRegion(); - xr.x = 0; - xr.y = 0; - xr.width = surf->width; - xr.height = surf->height; - XUnionRectWithRegion (&xr, xregion, xregion); - rects = malloc(sizeof(XRectangle)); - if (rects == NULL) - return CAIRO_STATUS_NO_MEMORY; - rects[0] = xr; - m = 1; - - } else { - n = pixman_region_num_rects (region); - /* XXX: Are we sure these are the semantics we want for an - * empty, (not null) region? */ - if (n == 0) - return CAIRO_STATUS_SUCCESS; - rects = malloc(sizeof(XRectangle) * n); - if (rects == NULL) - return CAIRO_STATUS_NO_MEMORY; - box = pixman_region_rects (region); - xregion = XCreateRegion(); - - m = n; - for (; n > 0; --n, ++box) { - xr.x = (short) box->x1; - xr.y = (short) box->y1; - xr.width = (unsigned short) (box->x2 - box->x1); - xr.height = (unsigned short) (box->y2 - box->y1); - rects[n-1] = xr; - XUnionRectWithRegion (&xr, xregion, xregion); - } - } - - _cairo_xlib_surface_ensure_gc (surf); - XGetGCValues(surf->dpy, surf->gc, GCGraphicsExposures, &gc_values); - XSetGraphicsExposures(surf->dpy, surf->gc, False); - XSetClipRectangles(surf->dpy, surf->gc, 0, 0, rects, m, Unsorted); - free(rects); - if (surf->picture) - XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); - XDestroyRegion(xregion); - XSetGraphicsExposures(surf->dpy, surf->gc, gc_values.graphics_exposures); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs); - -static const cairo_surface_backend_t cairo_xlib_surface_backend = { - _cairo_xlib_surface_create_similar, - _cairo_xlib_surface_destroy, - _cairo_xlib_surface_pixels_per_inch, - _cairo_xlib_surface_acquire_source_image, - _cairo_xlib_surface_release_source_image, - _cairo_xlib_surface_acquire_dest_image, - _cairo_xlib_surface_release_dest_image, - _cairo_xlib_surface_clone_similar, - _cairo_xlib_surface_composite, - _cairo_xlib_surface_fill_rectangles, - _cairo_xlib_surface_composite_trapezoids, - _cairo_xlib_surface_copy_page, - _cairo_xlib_surface_show_page, - _cairo_xlib_surface_set_clip_region, - _cairo_xlib_surface_show_glyphs -}; - -static cairo_surface_t * -_cairo_xlib_surface_create_with_size (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap, - int width, - int height) -{ - cairo_xlib_surface_t *surface; - int render_standard; - - surface = malloc (sizeof (cairo_xlib_surface_t)); - if (surface == NULL) - return NULL; - - _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend); - - surface->visual = visual; - surface->format = format; - - surface->dpy = dpy; - - surface->gc = 0; - surface->drawable = drawable; - surface->owns_pixmap = 0; - surface->visual = visual; - surface->width = width; - surface->height = height; - - if (! XRenderQueryVersion (dpy, &surface->render_major, &surface->render_minor)) { - surface->render_major = -1; - surface->render_minor = -1; - } - - switch (format) { - case CAIRO_FORMAT_A1: - render_standard = PictStandardA1; - break; - case CAIRO_FORMAT_A8: - render_standard = PictStandardA8; - break; - case CAIRO_FORMAT_RGB24: - render_standard = PictStandardRGB24; - break; - case CAIRO_FORMAT_ARGB32: - default: - render_standard = PictStandardARGB32; - break; - } - - /* XXX: I'm currently ignoring the colormap. Is that bad? */ - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) - surface->picture = XRenderCreatePicture (dpy, drawable, - visual ? - XRenderFindVisualFormat (dpy, visual) : - XRenderFindStandardFormat (dpy, render_standard), - 0, NULL); - else - surface->picture = 0; - - return (cairo_surface_t *) surface; -} - -cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - Visual *visual, - cairo_format_t format, - Colormap colormap) -{ - Window window_ignore; - unsigned int int_ignore; - unsigned int width, height; - - /* XXX: This call is a round-trip. We probably want to instead (or - * also?) export a version that accepts width/height. Then, we'll - * likely also need a resize function too. - */ - XGetGeometry(dpy, drawable, - &window_ignore, &int_ignore, &int_ignore, - &width, &height, - &int_ignore, &int_ignore); - - return _cairo_xlib_surface_create_with_size (dpy, drawable, visual, format, - colormap, width, height); -} -DEPRECATE (cairo_surface_create_for_drawable, cairo_xlib_surface_create); - -/* RENDER glyphset cache code */ - -typedef struct glyphset_cache { - cairo_cache_t base; - struct glyphset_cache *next; - Display *display; - XRenderPictFormat *a8_pict_format; - GlyphSet glyphset; - Glyph counter; -} glyphset_cache_t; - -typedef struct { - cairo_glyph_cache_key_t key; - Glyph glyph; - XGlyphInfo info; - int refcount; -} glyphset_cache_entry_t; - -static Glyph -_next_xlib_glyph (glyphset_cache_t *cache) -{ - return ++(cache->counter); -} - - -static cairo_status_t -_xlib_glyphset_cache_create_entry (void *cache, - void *key, - void **return_entry) -{ - glyphset_cache_t *g = (glyphset_cache_t *) cache; - cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *)key; - glyphset_cache_entry_t *v; - - cairo_status_t status; - - cairo_cache_t *im_cache; - cairo_image_glyph_cache_entry_t *im; - - v = malloc (sizeof (glyphset_cache_entry_t)); - _cairo_lock_global_image_glyph_cache (); - im_cache = _cairo_get_global_image_glyph_cache (); - - if (g == NULL || v == NULL || im_cache == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - status = _cairo_cache_lookup (im_cache, key, (void **) (&im), NULL); - if (status != CAIRO_STATUS_SUCCESS || im == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - v->refcount = 1; - v->key = *k; - _cairo_unscaled_font_reference (v->key.unscaled); - - v->glyph = _next_xlib_glyph (g); - - v->info.width = im->image ? im->image->stride : im->size.width; - v->info.height = im->size.height; - - /* - * Most of the font rendering system thinks of glyph tiles as having - * an origin at (0,0) and an x and y bounding box "offset" which - * extends possibly off into negative coordinates, like so: - * - * - * (x,y) <-- probably negative numbers - * +----------------+ - * | . | - * | . | - * |......(0,0) | - * | | - * | | - * +----------------+ - * (width+x,height+y) - * - * This is a postscript-y model, where each glyph has its own - * coordinate space, so it's what we expose in terms of metrics. It's - * apparantly what everyone's expecting. Everyone except the Render - * extension. Render wants to see a glyph tile starting at (0,0), with - * an origin offset inside, like this: - * - * (0,0) - * +---------------+ - * | . | - * | . | - * |......(x,y) | - * | | - * | | - * +---------------+ - * (width,height) - * - * Luckily, this is just the negation of the numbers we already have - * sitting around for x and y. - */ - - v->info.x = -im->size.x; - v->info.y = -im->size.y; - v->info.xOff = 0; - v->info.yOff = 0; - - XRenderAddGlyphs (g->display, g->glyphset, - &(v->glyph), &(v->info), 1, - im->image ? im->image->data : NULL, - im->image ? v->info.height * v->info.width : 0); - - v->key.base.memory = im->image ? im->image->width * im->image->stride : 0; - *return_entry = v; - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_SUCCESS; -} - -static void -_glyphset_cache_entry_reference (glyphset_cache_entry_t *e) -{ - e->refcount++; -} - -static void -_xlib_glyphset_cache_destroy_cache (void *cache) -{ - /* FIXME: will we ever release glyphset caches? */ -} - -static void -_xlib_glyphset_cache_destroy_entry (void *cache, void *entry) -{ - glyphset_cache_t *g; - glyphset_cache_entry_t *v; - - g = (glyphset_cache_t *) cache; - v = (glyphset_cache_entry_t *) entry; - - if (--v->refcount > 0) - return; - - _cairo_unscaled_font_destroy (v->key.unscaled); - XRenderFreeGlyphs (g->display, g->glyphset, &(v->glyph), 1); - free (v); -} - -static const cairo_cache_backend_t _xlib_glyphset_cache_backend = { - _cairo_glyph_cache_hash, - _cairo_glyph_cache_keys_equal, - _xlib_glyphset_cache_create_entry, - _xlib_glyphset_cache_destroy_entry, - _xlib_glyphset_cache_destroy_cache -}; - - -static glyphset_cache_t * -_xlib_glyphset_caches = NULL; - -static void -_lock_xlib_glyphset_caches (void) -{ - /* FIXME: implement locking */ -} - -static void -_unlock_xlib_glyphset_caches (void) -{ - /* FIXME: implement locking */ -} - -static glyphset_cache_t * -_get_glyphset_cache (Display *d) -{ - /* - * There should usually only be one, or a very small number, of - * displays. So we just do a linear scan. - */ - - glyphset_cache_t *g; - - for (g = _xlib_glyphset_caches; g != NULL; g = g->next) { - if (g->display == d) - return g; - } - - g = malloc (sizeof (glyphset_cache_t)); - if (g == NULL) - goto ERR; - - g->counter = 0; - g->display = d; - g->a8_pict_format = XRenderFindStandardFormat (d, PictStandardA8); - if (g->a8_pict_format == NULL) - goto ERR; - - if (_cairo_cache_init (&g->base, - &_xlib_glyphset_cache_backend, - CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT)) - goto FREE_GLYPHSET_CACHE; - - g->glyphset = XRenderCreateGlyphSet (d, g->a8_pict_format); - g->next = _xlib_glyphset_caches; - _xlib_glyphset_caches = g; - return g; - - FREE_GLYPHSET_CACHE: - free (g); - - ERR: - return NULL; -} - -#define N_STACK_BUF 1024 - -static cairo_status_t -_cairo_xlib_surface_show_glyphs32 (cairo_font_t *font, - cairo_operator_t operator, - glyphset_cache_t *g, - cairo_glyph_cache_key_t *key, - cairo_xlib_surface_t *src, - cairo_xlib_surface_t *self, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - glyphset_cache_entry_t **entries, - int num_glyphs) -{ - XGlyphElt32 *elts = NULL; - XGlyphElt32 stack_elts [N_STACK_BUF]; - - unsigned int *chars = NULL; - unsigned int stack_chars [N_STACK_BUF]; - - int i; - int thisX, thisY; - int lastX = 0, lastY = 0; - - /* Acquire arrays of suitable sizes. */ - if (num_glyphs < N_STACK_BUF) { - elts = stack_elts; - chars = stack_chars; - - } else { - elts = malloc (num_glyphs * sizeof (XGlyphElt32)); - if (elts == NULL) - goto FAIL; - - chars = malloc (num_glyphs * sizeof (unsigned int)); - if (chars == NULL) - goto FREE_ELTS; - - } - - for (i = 0; i < num_glyphs; ++i) { - chars[i] = entries[i]->glyph; - elts[i].chars = &(chars[i]); - elts[i].nchars = 1; - elts[i].glyphset = g->glyphset; - thisX = (int) floor (glyphs[i].x + 0.5); - thisY = (int) floor (glyphs[i].y + 0.5); - elts[i].xOff = thisX - lastX; - elts[i].yOff = thisY - lastY; - lastX = thisX; - lastY = thisY; - } - - XRenderCompositeText32 (self->dpy, - _render_operator (operator), - src->picture, - self->picture, - g->a8_pict_format, - source_x, source_y, - 0, 0, - elts, num_glyphs); - - if (num_glyphs >= N_STACK_BUF) { - free (chars); - free (elts); - } - - return CAIRO_STATUS_SUCCESS; - - FREE_ELTS: - if (num_glyphs >= N_STACK_BUF) - free (elts); - - FAIL: - return CAIRO_STATUS_NO_MEMORY; -} - - -static cairo_status_t -_cairo_xlib_surface_show_glyphs16 (cairo_font_t *font, - cairo_operator_t operator, - glyphset_cache_t *g, - cairo_glyph_cache_key_t *key, - cairo_xlib_surface_t *src, - cairo_xlib_surface_t *self, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - glyphset_cache_entry_t **entries, - int num_glyphs) -{ - XGlyphElt16 *elts = NULL; - XGlyphElt16 stack_elts [N_STACK_BUF]; - - unsigned short *chars = NULL; - unsigned short stack_chars [N_STACK_BUF]; - - int i; - int thisX, thisY; - int lastX = 0, lastY = 0; - - /* Acquire arrays of suitable sizes. */ - if (num_glyphs < N_STACK_BUF) { - elts = stack_elts; - chars = stack_chars; - - } else { - elts = malloc (num_glyphs * sizeof (XGlyphElt16)); - if (elts == NULL) - goto FAIL; - - chars = malloc (num_glyphs * sizeof (unsigned short)); - if (chars == NULL) - goto FREE_ELTS; - - } - - for (i = 0; i < num_glyphs; ++i) { - chars[i] = entries[i]->glyph; - elts[i].chars = &(chars[i]); - elts[i].nchars = 1; - elts[i].glyphset = g->glyphset; - thisX = (int) floor (glyphs[i].x + 0.5); - thisY = (int) floor (glyphs[i].y + 0.5); - elts[i].xOff = thisX - lastX; - elts[i].yOff = thisY - lastY; - lastX = thisX; - lastY = thisY; - } - - XRenderCompositeText16 (self->dpy, - _render_operator (operator), - src->picture, - self->picture, - g->a8_pict_format, - source_x, source_y, - 0, 0, - elts, num_glyphs); - - if (num_glyphs >= N_STACK_BUF) { - free (chars); - free (elts); - } - - return CAIRO_STATUS_SUCCESS; - - FREE_ELTS: - if (num_glyphs >= N_STACK_BUF) - free (elts); - - FAIL: - return CAIRO_STATUS_NO_MEMORY; -} - -static cairo_status_t -_cairo_xlib_surface_show_glyphs8 (cairo_font_t *font, - cairo_operator_t operator, - glyphset_cache_t *g, - cairo_glyph_cache_key_t *key, - cairo_xlib_surface_t *src, - cairo_xlib_surface_t *self, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - glyphset_cache_entry_t **entries, - int num_glyphs) -{ - XGlyphElt8 *elts = NULL; - XGlyphElt8 stack_elts [N_STACK_BUF]; - - char *chars = NULL; - char stack_chars [N_STACK_BUF]; - - int i; - int thisX, thisY; - int lastX = 0, lastY = 0; - - /* Acquire arrays of suitable sizes. */ - if (num_glyphs < N_STACK_BUF) { - elts = stack_elts; - chars = stack_chars; - - } else { - elts = malloc (num_glyphs * sizeof (XGlyphElt8)); - if (elts == NULL) - goto FAIL; - - chars = malloc (num_glyphs * sizeof (char)); - if (chars == NULL) - goto FREE_ELTS; - - } - - for (i = 0; i < num_glyphs; ++i) { - chars[i] = entries[i]->glyph; - elts[i].chars = &(chars[i]); - elts[i].nchars = 1; - elts[i].glyphset = g->glyphset; - thisX = (int) floor (glyphs[i].x + 0.5); - thisY = (int) floor (glyphs[i].y + 0.5); - elts[i].xOff = thisX - lastX; - elts[i].yOff = thisY - lastY; - lastX = thisX; - lastY = thisY; - } - - XRenderCompositeText8 (self->dpy, - _render_operator (operator), - src->picture, - self->picture, - g->a8_pict_format, - source_x, source_y, - 0, 0, - elts, num_glyphs); - - if (num_glyphs >= N_STACK_BUF) { - free (chars); - free (elts); - } - - return CAIRO_STATUS_SUCCESS; - - FREE_ELTS: - if (num_glyphs >= N_STACK_BUF) - free (elts); - - FAIL: - return CAIRO_STATUS_NO_MEMORY; -} - - -static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - void *abstract_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_surface_attributes_t attributes; - cairo_int_status_t status; - unsigned int elt_size; - cairo_xlib_surface_t *self = abstract_surface; - cairo_xlib_surface_t *src; - glyphset_cache_t *g; - cairo_glyph_cache_key_t key; - glyphset_cache_entry_t **entries; - glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; - int i; - - if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (self)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pattern_acquire_surface (pattern, &self->base, - source_x, source_y, width, height, - (cairo_surface_t **) &src, - &attributes); - if (status) - return status; - - status = _cairo_xlib_surface_set_attributes (src, &attributes); - if (status) - goto FAIL; - - /* Acquire an entry array of suitable size. */ - if (num_glyphs < N_STACK_BUF) { - entries = stack_entries; - - } else { - entries = malloc (num_glyphs * sizeof (glyphset_cache_entry_t *)); - if (entries == NULL) - goto FAIL; - } - - _lock_xlib_glyphset_caches (); - g = _get_glyphset_cache (self->dpy); - if (g == NULL) - goto UNLOCK; - - /* Work out the index size to use. */ - elt_size = 8; - _cairo_font_get_glyph_cache_key (font, &key); - - for (i = 0; i < num_glyphs; ++i) { - key.index = glyphs[i].index; - status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i]), NULL); - if (status != CAIRO_STATUS_SUCCESS || entries[i] == NULL) - goto UNLOCK; - - /* Referencing the glyph entries we use prevents them from - * being freed if lookup of later entries causes them to - * be ejected from the cache. It would be more efficient - * (though more complex) to prevent them from being ejected - * from the cache at all, so they could get reused later - * in the same string. - */ - _glyphset_cache_entry_reference (entries[i]); - - if (elt_size == 8 && entries[i]->glyph > 0xff) - elt_size = 16; - if (elt_size == 16 && entries[i]->glyph > 0xffff) { - elt_size = 32; - break; - } - } - - /* Call the appropriate sub-function. */ - - if (elt_size == 8) - status = _cairo_xlib_surface_show_glyphs8 (font, operator, g, &key, src, self, - source_x + attributes.x_offset, - source_y + attributes.y_offset, - glyphs, entries, num_glyphs); - else if (elt_size == 16) - status = _cairo_xlib_surface_show_glyphs16 (font, operator, g, &key, src, self, - source_x + attributes.x_offset, - source_y + attributes.y_offset, - glyphs, entries, num_glyphs); - else - status = _cairo_xlib_surface_show_glyphs32 (font, operator, g, &key, src, self, - source_x + attributes.x_offset, - source_y + attributes.y_offset, - glyphs, entries, num_glyphs); - - for (i = 0; i < num_glyphs; ++i) - _xlib_glyphset_cache_destroy_entry (g, entries[i]); - - UNLOCK: - _unlock_xlib_glyphset_caches (); - - if (num_glyphs >= N_STACK_BUF) - free (entries); - - FAIL: - _cairo_pattern_release_surface (&self->base, &src->base, &attributes); - - return status; -} diff --git a/src/cairoint.h b/src/cairoint.h index 50899eca8..90d59b2cb 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -52,11 +53,17 @@ #include <assert.h> #include <stdlib.h> #include <string.h> +#include <stdarg.h> + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif #include <math.h> #include <limits.h> -#include <stdint.h> +#include <stdio.h> #include "cairo.h" +#include <pixman.h> #if __GNUC__ >= 3 && defined(__ELF__) # define slim_hidden_proto(name) slim_hidden_proto1(name, INT_##name) @@ -85,7 +92,7 @@ #define cairo_private #endif -/* These macros allow us to deprecate a function by providing an alias +/* This macro allow us to deprecate a function by providing an alias for the old function name to the new function name. With this macro, binary compatibility is preserved. The macro only works on some platforms --- tough. @@ -94,14 +101,15 @@ source code so that it will no longer link against the old symbols. Instead it will give a descriptive error message indicating that the old function has been deprecated by the new - function. */ + function. +*/ #if __GNUC__ >= 2 && defined(__ELF__) -# define DEPRECATE(old, new) \ +# define CAIRO_FUNCTION_ALIAS(old, new) \ extern __typeof (new) old \ __asm__ ("" #old) \ __attribute__((__alias__("" #new))) #else -# define DEPRECATE(old, new) +# define CAIRO_FUNCTION_ALIAS(old, new) #endif #ifndef __GNUC__ @@ -119,6 +127,12 @@ #define TRUE 1 #endif +#define ASSERT_NOT_REACHED \ +do { \ + static const int NOT_REACHED = 0; \ + assert (NOT_REACHED); \ +} while (0) + #include "cairo-wideint.h" typedef int32_t cairo_fixed_16_16_t; @@ -164,7 +178,7 @@ typedef struct _cairo_trapezoid { cairo_line_t left, right; } cairo_trapezoid_t; -typedef struct _cairo_rectangle_int { +typedef struct _cairo_rectangle { short x, y; unsigned short width, height; } cairo_rectangle_t, cairo_glyph_size_t; @@ -179,45 +193,12 @@ typedef enum cairo_int_status { #define CAIRO_OK(status) ((status) == CAIRO_STATUS_SUCCESS) -typedef enum cairo_path_op { - CAIRO_PATH_OP_MOVE_TO = 0, - CAIRO_PATH_OP_LINE_TO = 1, - CAIRO_PATH_OP_CURVE_TO = 2, - CAIRO_PATH_OP_CLOSE_PATH = 3 -} __attribute__ ((packed)) cairo_path_op_t; /* Don't want 32 bits if we can avoid it. */ - typedef enum cairo_direction { CAIRO_DIRECTION_FORWARD, CAIRO_DIRECTION_REVERSE } cairo_direction_t; -#define CAIRO_PATH_BUF_SZ 64 - -typedef struct _cairo_path_op_buf { - int num_ops; - cairo_path_op_t op[CAIRO_PATH_BUF_SZ]; - - struct _cairo_path_op_buf *next, *prev; -} cairo_path_op_buf_t; - -typedef struct _cairo_path_arg_buf { - int num_points; - cairo_point_t points[CAIRO_PATH_BUF_SZ]; - - struct _cairo_path_arg_buf *next, *prev; -} cairo_path_arg_buf_t; - -typedef struct _cairo_path { - cairo_path_op_buf_t *op_head; - cairo_path_op_buf_t *op_tail; - - cairo_path_arg_buf_t *arg_head; - cairo_path_arg_buf_t *arg_tail; - - cairo_point_t last_move_point; - cairo_point_t current_point; - int has_current_point; -} cairo_path_t; +typedef struct _cairo_path_fixed cairo_path_fixed_t; typedef struct _cairo_edge { cairo_line_t edge; @@ -302,6 +283,24 @@ _cairo_array_copy_element (cairo_array_t *array, int index, void *dst); cairo_private int _cairo_array_num_elements (cairo_array_t *array); +typedef cairo_array_t cairo_user_data_array_t; + +cairo_private void +_cairo_user_data_array_init (cairo_user_data_array_t *array); + +cairo_private void +_cairo_user_data_array_destroy (cairo_user_data_array_t *array); + +cairo_private void * +_cairo_user_data_array_get_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key); + +cairo_private cairo_status_t +_cairo_user_data_array_set_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + /* cairo_cache.c structures and functions */ typedef struct _cairo_cache_backend { @@ -404,25 +403,34 @@ _cairo_hash_string (const char *c); #define CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 #define CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 -typedef struct { - double matrix[2][2]; -} cairo_font_scale_t; +typedef struct _cairo_unscaled_font cairo_unscaled_font_t; -struct _cairo_font_backend; +typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; +typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; +typedef struct _cairo_font_face_backend cairo_font_face_backend_t; /* * A cairo_unscaled_font_t is just an opaque handle we use in the * glyph cache. */ -typedef struct { +struct _cairo_unscaled_font { + int refcount; + const cairo_unscaled_font_backend_t *backend; +}; + +struct _cairo_scaled_font { int refcount; - const struct _cairo_font_backend *backend; -} cairo_unscaled_font_t; + cairo_matrix_t font_matrix; /* font space => user space */ + cairo_matrix_t ctm; /* user space => device space */ + cairo_matrix_t scale; /* font space => device space */ + cairo_font_face_t *font_face; /* may be NULL */ + const cairo_scaled_font_backend_t *backend; +}; -struct _cairo_font { +struct _cairo_font_face { int refcount; - cairo_font_scale_t scale; /* font space => device space */ - const struct _cairo_font_backend *backend; + cairo_user_data_array_t user_data; + const cairo_font_face_backend_t *backend; }; /* cairo_font.c is responsible for a global glyph cache: @@ -438,7 +446,7 @@ struct _cairo_font { typedef struct { cairo_cache_entry_base_t base; cairo_unscaled_font_t *unscaled; - cairo_font_scale_t scale; + cairo_matrix_t scale; /* translation is ignored */ int flags; unsigned long index; } cairo_glyph_cache_key_t; @@ -471,98 +479,109 @@ _cairo_glyph_cache_keys_equal (void *cache, /* the font backend interface */ -typedef struct _cairo_font_backend { - cairo_status_t (*create) (const char *family, +struct _cairo_unscaled_font_backend { + void (*destroy) (void *unscaled_font); + cairo_status_t (*create_glyph) (void *unscaled_font, + cairo_image_glyph_cache_entry_t *entry); +}; + +struct _cairo_scaled_font_backend { + cairo_status_t (*create) (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight, - cairo_font_scale_t *scale, - cairo_font_t **font); - - void (*destroy_font) (void *font); - void (*destroy_unscaled_font) (void *font); - - cairo_status_t (*font_extents) (void *font, - cairo_font_extents_t *extents); - - cairo_status_t (*text_to_glyphs) (void *font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs); - - cairo_status_t (*glyph_extents) (void *font, - cairo_glyph_t *glyphs, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **font); + + void (*destroy) (void *font); + + cairo_status_t (*font_extents) (void *font, + cairo_font_extents_t *extents); + + cairo_status_t (*text_to_glyphs) (void *font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); + + cairo_status_t (*glyph_extents) (void *font, + cairo_glyph_t *glyphs, int num_glyphs, - cairo_text_extents_t *extents); + cairo_text_extents_t *extents); - cairo_status_t (*glyph_bbox) (void *font, - const cairo_glyph_t *glyphs, + cairo_status_t (*glyph_bbox) (void *font, + const cairo_glyph_t *glyphs, int num_glyphs, - cairo_box_t *bbox); + cairo_box_t *bbox); - cairo_status_t (*show_glyphs) (void *font, + cairo_status_t (*show_glyphs) (void *font, cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, + cairo_pattern_t *pattern, + cairo_surface_t *surface, int source_x, int source_y, int dest_x, int dest_y, unsigned int width, unsigned int height, - const cairo_glyph_t *glyphs, + const cairo_glyph_t *glyphs, int num_glyphs); - cairo_status_t (*glyph_path) (void *font, - cairo_glyph_t *glyphs, + cairo_status_t (*glyph_path) (void *font, + cairo_glyph_t *glyphs, int num_glyphs, - cairo_path_t *path); - void (*get_glyph_cache_key) (void *font, - cairo_glyph_cache_key_t *key); - - cairo_status_t (*create_glyph) (cairo_image_glyph_cache_entry_t *entry); + cairo_path_fixed_t *path); + void (*get_glyph_cache_key) (void *font, + cairo_glyph_cache_key_t *key); +}; -} cairo_font_backend_t; +struct _cairo_font_face_backend { + /* The destroy() function is allowed to resurrect the font face + * by re-referencing. This is needed for the FreeType backend. + */ + void (*destroy) (void *font_face); + cairo_status_t (*create_font) (void *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + cairo_scaled_font_t **scaled_font); +}; /* concrete font backends */ -#ifdef CAIRO_HAS_FT_FONT +#if CAIRO_HAS_FT_FONT -extern const cairo_private struct _cairo_font_backend cairo_ft_font_backend; +extern const cairo_private struct _cairo_scaled_font_backend cairo_ft_scaled_font_backend; #endif -#ifdef CAIRO_HAS_WIN32_FONT +#if CAIRO_HAS_WIN32_FONT -extern const cairo_private struct _cairo_font_backend cairo_win32_font_backend; +extern const cairo_private struct _cairo_scaled_font_backend cairo_win32_scaled_font_backend; #endif -#ifdef CAIRO_HAS_ATSUI_FONT +#if CAIRO_HAS_ATSUI_FONT -extern const cairo_private struct _cairo_font_backend cairo_atsui_font_backend; +extern const cairo_private struct _cairo_scaled_font_backend cairo_atsui_scaled_font_backend; #endif typedef struct _cairo_surface_backend { cairo_surface_t * (*create_similar) (void *surface, - cairo_format_t format, - int drawable, - int width, - int height); + cairo_format_t format, + int drawable, + int width, + int height); - void - (*destroy) (void *surface); - - double - (*pixels_per_inch) (void *surface); + cairo_status_t + (*finish) (void *surface); cairo_status_t - (* acquire_source_image) (void *abstract_surface, + (*acquire_source_image) (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra); void - (* release_source_image) (void *abstract_surface, + (*release_source_image) (void *abstract_surface, cairo_image_surface_t *image, void *image_extra); @@ -587,39 +606,39 @@ typedef struct _cairo_surface_backend { /* XXX: dst should be the first argument for consistency */ cairo_int_status_t - (*composite) (cairo_operator_t operator, + (*composite) (cairo_operator_t operator, cairo_pattern_t *src, cairo_pattern_t *mask, void *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height); + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height); cairo_int_status_t (*fill_rectangles) (void *surface, - cairo_operator_t operator, + cairo_operator_t operator, const cairo_color_t *color, cairo_rectangle_t *rects, - int num_rects); + int num_rects); /* XXX: dst should be the first argument for consistency */ cairo_int_status_t - (*composite_trapezoids) (cairo_operator_t operator, + (*composite_trapezoids) (cairo_operator_t operator, cairo_pattern_t *pattern, void *dst, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, - int num_traps); + int num_traps); cairo_int_status_t (*copy_page) (void *surface); @@ -630,29 +649,47 @@ typedef struct _cairo_surface_backend { cairo_int_status_t (*set_clip_region) (void *surface, pixman_region16_t *region); + + /* Get the extents of the current surface. For many surface types + * this will be as simple as { x=0, y=0, width=surface->width, + * height=surface->height}. + * + * This function need not take account of any clipping from + * set_clip_region since the generic version of set_clip_region + * saves those, and the generic get_clip_extents will only call + * into the specific surface->get_extents if there is no current + * clip. + */ + cairo_int_status_t + (*get_extents) (void *surface, + cairo_rectangle_t *rectangle); + /* * This is an optional entry to let the surface manage its own glyph * resources. If null, the font will be asked to render against this * surface, using image surfaces as glyphs. */ - cairo_status_t - (*show_glyphs) (cairo_font_t *font, - cairo_operator_t operator, + cairo_int_status_t + (*show_glyphs) (cairo_scaled_font_t *font, + cairo_operator_t operator, cairo_pattern_t *pattern, void *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, - int num_glyphs); -} cairo_surface_backend_t; + int num_glyphs); -struct _cairo_matrix { - double m[3][2]; -}; + cairo_int_status_t + (*fill_path) (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *dst, + cairo_path_fixed_t *path); + +} cairo_surface_backend_t; typedef struct _cairo_format_masks { int bpp; @@ -662,14 +699,26 @@ typedef struct _cairo_format_masks { unsigned long blue_mask; } cairo_format_masks_t; +typedef struct _cairo_surface_save cairo_surface_save_t; + struct _cairo_surface { const cairo_surface_backend_t *backend; unsigned int ref_count; + cairo_bool_t finished; + cairo_user_data_array_t user_data; cairo_matrix_t matrix; cairo_filter_t filter; int repeat; + + double device_x_offset; + double device_y_offset; + + cairo_surface_save_t *saves; /* Stack of saved states from cairo_surface_begin/end() */ + int level; /* Number saved states */ + + pixman_region16_t *clip_region; }; struct _cairo_image_surface { @@ -677,7 +726,7 @@ struct _cairo_image_surface { /* libic-specific fields */ cairo_format_t format; - char *data; + unsigned char *data; int owns_data; int width; @@ -706,6 +755,12 @@ struct _cairo_color { unsigned short alpha_short; }; +typedef enum { + CAIRO_STOCK_WHITE, + CAIRO_STOCK_BLACK, + CAIRO_STOCK_TRANSPARENT +} cairo_stock_t; + #define CAIRO_EXTEND_DEFAULT CAIRO_EXTEND_NONE #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_BEST @@ -727,13 +782,11 @@ struct _cairo_pattern { cairo_matrix_t matrix; cairo_filter_t filter; cairo_extend_t extend; - double alpha; }; typedef struct _cairo_solid_pattern { cairo_pattern_t base; - - double red, green, blue; + cairo_color_t color; } cairo_solid_pattern_t; typedef struct _cairo_surface_pattern { @@ -787,6 +840,7 @@ typedef struct _cairo_surface_attributes { int x_offset; int y_offset; cairo_bool_t acquired; + cairo_bool_t clip_saved; void *extra; } cairo_surface_attributes_t; @@ -800,20 +854,20 @@ typedef struct _cairo_traps { #define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL #define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL -#if defined (CAIRO_HAS_WIN32_FONT) +#if CAIRO_HAS_WIN32_FONT #define CAIRO_FONT_FAMILY_DEFAULT "Arial" -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_win32_font_backend +#define CAIRO_FONT_BACKEND_DEFAULT &cairo_win32_scaled_font_backend -#elif defined (CAIRO_HAS_ATSUI_FONT) +#elif CAIRO_HAS_ATSUI_FONT #define CAIRO_FONT_FAMILY_DEFAULT "Monaco" -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_atsui_font_backend +#define CAIRO_FONT_BACKEND_DEFAULT &cairo_atsui_scaled_font_backend -#elif defined (CAIRO_HAS_FT_FONT) +#elif CAIRO_HAS_FT_FONT #define CAIRO_FONT_FAMILY_DEFAULT "serif" -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_ft_font_backend +#define CAIRO_FONT_BACKEND_DEFAULT &cairo_ft_scaled_font_backend #endif @@ -824,7 +878,7 @@ typedef struct _cairo_traps { #define CAIRO_GSTATE_LINE_CAP_DEFAULT CAIRO_LINE_CAP_BUTT #define CAIRO_GSTATE_LINE_JOIN_DEFAULT CAIRO_LINE_JOIN_MITER #define CAIRO_GSTATE_MITER_LIMIT_DEFAULT 10.0 -#define CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT 96.0 +#define CAIRO_GSTATE_DEFAULT_FONT_SIZE 10.0 /* Need a name distinct from the cairo_clip function */ typedef struct _cairo_clip_rec { @@ -833,55 +887,7 @@ typedef struct _cairo_clip_rec { cairo_surface_t *surface; } cairo_clip_rec_t; -typedef struct _cairo_gstate { - cairo_operator_t operator; - - double tolerance; - - /* stroke style */ - double line_width; - cairo_line_cap_t line_cap; - cairo_line_join_t line_join; - double miter_limit; - - cairo_fill_rule_t fill_rule; - - double *dash; - int num_dashes; - double dash_offset; - - char *font_family; /* NULL means CAIRO_FONT_FAMILY_DEFAULT; */ - cairo_font_slant_t font_slant; - cairo_font_weight_t font_weight; - - cairo_font_t *font; /* Specific to the current CTM */ - - cairo_surface_t *surface; - - cairo_pattern_t *pattern; - double alpha; - - cairo_clip_rec_t clip; - - double pixels_per_inch; - - cairo_matrix_t font_matrix; - - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; - - cairo_path_t path; - - cairo_pen_t pen_regular; - - struct _cairo_gstate *next; -} cairo_gstate_t; - -struct _cairo { - unsigned int ref_count; - cairo_gstate_t *gstate; - cairo_status_t status; -}; +typedef struct _cairo_gstate cairo_gstate_t; typedef struct _cairo_stroke_face { cairo_point_t ccw; @@ -920,13 +926,13 @@ _cairo_fixed_integer_floor (cairo_fixed_t f); cairo_private int _cairo_fixed_integer_ceil (cairo_fixed_t f); - /* cairo_gstate.c */ cairo_private cairo_gstate_t * -_cairo_gstate_create (void); +_cairo_gstate_create (cairo_surface_t *target); cairo_private cairo_status_t -_cairo_gstate_init (cairo_gstate_t *gstate); +_cairo_gstate_init (cairo_gstate_t *gstate, + cairo_surface_t *target); cairo_private cairo_status_t _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other); @@ -949,68 +955,54 @@ _cairo_gstate_begin_group (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_end_group (cairo_gstate_t *gstate); -cairo_private cairo_status_t -_cairo_gstate_set_target_surface (cairo_gstate_t *gstate, cairo_surface_t *surface); - cairo_private cairo_surface_t * -_cairo_gstate_current_target_surface (cairo_gstate_t *gstate); +_cairo_gstate_get_target (cairo_gstate_t *gstate); cairo_private cairo_status_t -_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern); +_cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source); + +cairo_status_t +_cairo_gstate_set_source_solid (cairo_gstate_t *gstate, + const cairo_color_t *color); cairo_private cairo_pattern_t * -_cairo_gstate_current_pattern (cairo_gstate_t *gstate); +_cairo_gstate_get_source (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator); cairo_private cairo_operator_t -_cairo_gstate_current_operator (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, double blue); - -cairo_private cairo_status_t -_cairo_gstate_current_rgb_color (cairo_gstate_t *gstate, - double *red, - double *green, - double *blue); +_cairo_gstate_get_operator (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance); cairo_private double -_cairo_gstate_current_tolerance (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_set_alpha (cairo_gstate_t *gstate, double alpha); - -cairo_private double -_cairo_gstate_current_alpha (cairo_gstate_t *gstate); +_cairo_gstate_get_tolerance (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule); cairo_private cairo_fill_rule_t -_cairo_gstate_current_fill_rule (cairo_gstate_t *gstate); +_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width); cairo_private double -_cairo_gstate_current_line_width (cairo_gstate_t *gstate); +_cairo_gstate_get_line_width (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap); cairo_private cairo_line_cap_t -_cairo_gstate_current_line_cap (cairo_gstate_t *gstate); +_cairo_gstate_get_line_cap (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join); cairo_private cairo_line_join_t -_cairo_gstate_current_line_join (cairo_gstate_t *gstate); +_cairo_gstate_get_line_join (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_dash (cairo_gstate_t *gstate, double *dash, int num_dashes, double offset); @@ -1019,10 +1011,10 @@ cairo_private cairo_status_t _cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit); cairo_private double -_cairo_gstate_current_miter_limit (cairo_gstate_t *gstate); +_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate); cairo_private void -_cairo_gstate_current_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); +_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty); @@ -1034,94 +1026,46 @@ cairo_private cairo_status_t _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle); cairo_private cairo_status_t -_cairo_gstate_concat_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix); - -cairo_private cairo_status_t -_cairo_gstate_set_matrix (cairo_gstate_t *gstate, - cairo_matrix_t *matrix); +_cairo_gstate_transform (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); cairo_private cairo_status_t -_cairo_gstate_default_matrix (cairo_gstate_t *gstate); +_cairo_gstate_set_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_gstate_identity_matrix (cairo_gstate_t *gstate); cairo_private cairo_status_t -_cairo_gstate_transform_point (cairo_gstate_t *gstate, double *x, double *y); - -cairo_private cairo_status_t -_cairo_gstate_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy); +_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y); cairo_private cairo_status_t -_cairo_gstate_inverse_transform_point (cairo_gstate_t *gstate, double *x, double *y); +_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy); cairo_private cairo_status_t -_cairo_gstate_inverse_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy); +_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y); cairo_private cairo_status_t -_cairo_gstate_new_path (cairo_gstate_t *gstate); +_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy); -cairo_private cairo_status_t -_cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y); - -cairo_private cairo_status_t -_cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y); - -cairo_private cairo_status_t -_cairo_gstate_curve_to (cairo_gstate_t *gstate, - double x1, double y1, - double x2, double y2, - double x3, double y3); - -cairo_private cairo_status_t -_cairo_gstate_arc (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle1, double angle2); - -cairo_private cairo_status_t -_cairo_gstate_arc_negative (cairo_gstate_t *gstate, - double xc, double yc, - double radius, - double angle1, double angle2); - -cairo_private cairo_status_t -_cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy); - -cairo_private cairo_status_t -_cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy); - -cairo_private cairo_status_t -_cairo_gstate_rel_curve_to (cairo_gstate_t *gstate, - double dx1, double dy1, - double dx2, double dy2, - double dx3, double dy3); - -/* XXX: NYI -cairo_private cairo_status_t -_cairo_gstate_stroke_path (cairo_gstate_t *gstate); -*/ +cairo_private void +_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y); -cairo_private cairo_status_t -_cairo_gstate_close_path (cairo_gstate_t *gstate); +cairo_private void +_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y); cairo_private cairo_status_t -_cairo_gstate_current_point (cairo_gstate_t *gstate, double *x, double *y); +_cairo_gstate_paint (cairo_gstate_t *gstate); cairo_private cairo_status_t -_cairo_gstate_interpret_path (cairo_gstate_t *gstate, - cairo_move_to_func_t *move_to, - cairo_line_to_func_t *line_to, - cairo_curve_to_func_t *curve_to, - cairo_close_path_func_t *close_path, - void *closure); +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask); cairo_private cairo_status_t -_cairo_gstate_stroke (cairo_gstate_t *gstate); +_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t -_cairo_gstate_fill (cairo_gstate_t *gstate); +_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_gstate_copy_page (cairo_gstate_t *gstate); @@ -1130,85 +1074,82 @@ cairo_private cairo_status_t _cairo_gstate_show_page (cairo_gstate_t *gstate); cairo_private cairo_status_t -_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2); cairo_private cairo_status_t -_cairo_gstate_fill_extents (cairo_gstate_t *gstate, +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2); cairo_private cairo_status_t -_cairo_gstate_in_stroke (cairo_gstate_t *gstate, - double x, - double y, - cairo_bool_t *inside_ret); +_cairo_gstate_in_stroke (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret); cairo_private cairo_status_t -_cairo_gstate_in_fill (cairo_gstate_t *gstate, - double x, - double y, - cairo_bool_t *inside_ret); +_cairo_gstate_in_fill (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret); cairo_private cairo_status_t -_cairo_gstate_init_clip (cairo_gstate_t *gstate); +_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate); - -cairo_private cairo_status_t -_cairo_gstate_restore_external_state (cairo_gstate_t *gstate); +_cairo_gstate_reset_clip (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_show_surface (cairo_gstate_t *gstate, cairo_surface_t *surface, - int width, - int height); + double x, + double y, + double width, + double height); cairo_private cairo_status_t -_cairo_gstate_select_font (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); +_cairo_gstate_select_font_face (cairo_gstate_t *gstate, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); cairo_private cairo_status_t -_cairo_gstate_scale_font (cairo_gstate_t *gstate, - double scale); +_cairo_gstate_set_font_size (cairo_gstate_t *gstate, + double size); -cairo_private void -_cairo_gstate_current_font_scale (cairo_gstate_t *gstate, - cairo_font_scale_t *sc); - +void +_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, + cairo_matrix_t *matrix); + cairo_private cairo_status_t -_cairo_gstate_transform_font (cairo_gstate_t *gstate, - cairo_matrix_t *matrix); +_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); cairo_private cairo_status_t -_cairo_gstate_current_font (cairo_gstate_t *gstate, - cairo_font_t **font); - -cairo_private void -_cairo_gstate_set_font_transform (cairo_gstate_t *gstate, - cairo_matrix_t *matrix); - -cairo_private void -_cairo_gstate_current_font_transform (cairo_gstate_t *gstate, - cairo_matrix_t *matrix); +_cairo_gstate_get_font_face (cairo_gstate_t *gstate, + cairo_font_face_t **font_face); cairo_private cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents); +_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents); cairo_private cairo_status_t -_cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font); +_cairo_gstate_set_font_face (cairo_gstate_t *gstate, + cairo_font_face_t *font_face); cairo_private cairo_status_t _cairo_gstate_text_to_glyphs (cairo_gstate_t *font, - const unsigned char *utf8, + const char *utf8, + double x, + double y, cairo_glyph_t **glyphs, - int *num_glyphs); + int *num_glyphs); cairo_private cairo_status_t _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, @@ -1222,45 +1163,70 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, int num_glyphs); cairo_private cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs); +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); /* cairo_color.c */ +cairo_private const cairo_color_t * +_cairo_stock_color (cairo_stock_t stock); + +#define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE) +#define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK) +#define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT) + cairo_private void _cairo_color_init (cairo_color_t *color); cairo_private void -_cairo_color_fini (cairo_color_t *color); +_cairo_color_init_rgb (cairo_color_t *color, + double red, double green, double blue); cairo_private void -_cairo_color_set_rgb (cairo_color_t *color, double red, double green, double blue); +_cairo_color_init_rgba (cairo_color_t *color, + double red, double green, double blue, + double alpha); cairo_private void -_cairo_color_get_rgb (const cairo_color_t *color, - double *red, double *green, double *blue); +_cairo_color_multiply_alpha (cairo_color_t *color, + double alpha); cairo_private void -_cairo_color_set_alpha (cairo_color_t *color, double alpha); +_cairo_color_get_rgba (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha); -/* cairo_font.c */ +cairo_private void +_cairo_color_get_rgba_premultiplied (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha); -cairo_private cairo_status_t -_cairo_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight, - cairo_font_scale_t *sc, - cairo_font_t **font); +/* cairo-font.c */ + +cairo_private void +_cairo_font_face_init (cairo_font_face_t *font_face, + const cairo_font_face_backend_t *backend); + +cairo_private cairo_font_face_t * +_cairo_simple_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); cairo_private void -_cairo_font_init (cairo_font_t *font, - cairo_font_scale_t *scale, - const cairo_font_backend_t *backend); +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_scaled_font_backend_t *backend); cairo_private void -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const struct _cairo_font_backend *backend); +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const cairo_unscaled_font_backend_t *backend); cairo_private void _cairo_unscaled_font_reference (cairo_unscaled_font_t *font); @@ -1269,56 +1235,50 @@ cairo_private void _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); cairo_private cairo_status_t -_cairo_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents); +_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); cairo_private cairo_status_t -_cairo_font_text_to_glyphs (cairo_font_t *font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs); +_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); cairo_private cairo_status_t -_cairo_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); +_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); cairo_private cairo_status_t -_cairo_font_glyph_bbox (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox); +_cairo_scaled_font_glyph_bbox (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox); cairo_private cairo_status_t -_cairo_font_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_pattern_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int widht, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs); +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs); cairo_private cairo_status_t -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path); - -cairo_private cairo_status_t -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path); +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); cairo_private void -_cairo_font_get_glyph_cache_key (cairo_font_t *font, - cairo_glyph_cache_key_t *key); +_cairo_scaled_font_get_glyph_cache_key (cairo_scaled_font_t *scaled_font, + cairo_glyph_cache_key_t *key); /* cairo_hull.c */ cairo_private cairo_status_t @@ -1326,76 +1286,97 @@ _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices); /* cairo_path.c */ cairo_private void -_cairo_path_init (cairo_path_t *path); +_cairo_path_fixed_init (cairo_path_fixed_t *path); cairo_private cairo_status_t -_cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other); +_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, + cairo_path_fixed_t *other); cairo_private void -_cairo_path_fini (cairo_path_t *path); +_cairo_path_fixed_fini (cairo_path_fixed_t *path); -cairo_private cairo_status_t -_cairo_path_move_to (cairo_path_t *path, cairo_point_t *point); +cairo_status_t +_cairo_path_fixed_move_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y); -cairo_private cairo_status_t -_cairo_path_rel_move_to (cairo_path_t *path, cairo_slope_t *slope); +cairo_status_t +_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy); -cairo_private cairo_status_t -_cairo_path_line_to (cairo_path_t *path, cairo_point_t *point); +cairo_status_t +_cairo_path_fixed_line_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y); -cairo_private cairo_status_t -_cairo_path_rel_line_to (cairo_path_t *path, cairo_slope_t *slope); +cairo_status_t +_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy); -cairo_private cairo_status_t -_cairo_path_curve_to (cairo_path_t *path, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2); +cairo_status_t +_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t x0, cairo_fixed_t y0, + cairo_fixed_t x1, cairo_fixed_t y1, + cairo_fixed_t x2, cairo_fixed_t y2); -cairo_private cairo_status_t -_cairo_path_rel_curve_to (cairo_path_t *path, - cairo_slope_t *s0, - cairo_slope_t *s1, - cairo_slope_t *s2); +cairo_status_t +_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t dx0, cairo_fixed_t dy0, + cairo_fixed_t dx1, cairo_fixed_t dy1, + cairo_fixed_t dx2, cairo_fixed_t dy2); cairo_private cairo_status_t -_cairo_path_close_path (cairo_path_t *path); +_cairo_path_fixed_close_path (cairo_path_fixed_t *path); -cairo_private cairo_status_t -_cairo_path_current_point (cairo_path_t *path, cairo_point_t *point); +cairo_status_t +_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, + cairo_fixed_t *x, + cairo_fixed_t *y); -typedef cairo_status_t (cairo_path_move_to_func_t) (void *closure, - cairo_point_t *point); +typedef cairo_status_t +(cairo_path_fixed_move_to_func_t) (void *closure, + cairo_point_t *point); -typedef cairo_status_t (cairo_path_line_to_func_t) (void *closure, - cairo_point_t *point); +typedef cairo_status_t +(cairo_path_fixed_line_to_func_t) (void *closure, + cairo_point_t *point); -typedef cairo_status_t (cairo_path_curve_to_func_t) (void *closure, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2); +typedef cairo_status_t +(cairo_path_fixed_curve_to_func_t) (void *closure, + cairo_point_t *p0, + cairo_point_t *p1, + cairo_point_t *p2); -typedef cairo_status_t (cairo_path_close_path_func_t) (void *closure); +typedef cairo_status_t +(cairo_path_fixed_close_path_func_t) (void *closure); cairo_private cairo_status_t -_cairo_path_interpret (cairo_path_t *path, - cairo_direction_t dir, - cairo_path_move_to_func_t *move_to, - cairo_path_line_to_func_t *line_to, - cairo_path_curve_to_func_t *curve_to, - cairo_path_close_path_func_t *close_path, - void *closure); +_cairo_path_fixed_interpret (cairo_path_fixed_t *path, + cairo_direction_t dir, + cairo_path_fixed_move_to_func_t *move_to, + cairo_path_fixed_line_to_func_t *line_to, + cairo_path_fixed_curve_to_func_t *curve_to, + cairo_path_fixed_close_path_func_t *close_path, + void *closure); cairo_private cairo_status_t -_cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2); +_cairo_path_fixed_bounds (cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); /* cairo_path_fill.c */ cairo_private cairo_status_t -_cairo_path_fill_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps); +_cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, + cairo_gstate_t *gstate, + cairo_traps_t *traps); /* cairo_path_stroke.c */ cairo_private cairo_status_t -_cairo_path_stroke_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps); +_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, + cairo_gstate_t *gstate, + cairo_traps_t *traps); /* cairo_surface.c */ cairo_private cairo_surface_t * @@ -1406,24 +1387,33 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, int height); cairo_private cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_format_t format, - int width, - int height, - cairo_color_t *color); +_cairo_surface_create_similar_solid (cairo_surface_t *other, + cairo_format_t format, + int width, + int height, + const cairo_color_t *color); cairo_private void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend); cairo_private cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t operator, - cairo_color_t *color, - int x, - int y, - int width, - int height); +_cairo_surface_begin (cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_surface_begin_reset_clip (cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_surface_end (cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_surface_fill_rectangle (cairo_surface_t *surface, + cairo_operator_t operator, + const cairo_color_t *color, + int x, + int y, + int width, + int height); cairo_private cairo_status_t _cairo_surface_composite (cairo_operator_t operator, @@ -1446,6 +1436,12 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_rectangle_t *rects, int num_rects); +cairo_private cairo_int_status_t +_cairo_surface_fill_path (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_path_fixed_t *path); + cairo_private cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t operator, cairo_pattern_t *pattern, @@ -1465,9 +1461,6 @@ _cairo_surface_copy_page (cairo_surface_t *surface); cairo_private cairo_status_t _cairo_surface_show_page (cairo_surface_t *surface); -cairo_private double -_cairo_surface_pixels_per_inch (cairo_surface_t *surface); - cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, @@ -1498,13 +1491,32 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_surface_t **clone_out); cairo_private cairo_status_t -_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region); +_cairo_surface_set_clip_region (cairo_surface_t *surface, + pixman_region16_t *region); + +cairo_private cairo_status_t +_cairo_surface_get_clip_extents (cairo_surface_t *surface, + cairo_rectangle_t *rectangle); + +cairo_private cairo_status_t +_cairo_surface_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs); /* cairo_image_surface.c */ cairo_private cairo_image_surface_t * -_cairo_image_surface_create_with_masks (char *data, - cairo_format_masks_t *format, +_cairo_image_surface_create_with_masks (unsigned char *data, + cairo_format_masks_t *format, int width, int height, int stride); @@ -1514,7 +1526,7 @@ _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); cairo_private cairo_status_t _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, - cairo_matrix_t *matrix); + const cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_image_surface_set_filter (cairo_image_surface_t *surface, @@ -1605,44 +1617,39 @@ _cairo_spline_fini (cairo_spline_t *spline); /* cairo_matrix.c */ cairo_private void -_cairo_matrix_init (cairo_matrix_t *matrix); +_cairo_matrix_get_affine (const cairo_matrix_t *matrix, + double *xx, double *yx, + double *xy, double *yy, + double *x0, double *y0); cairo_private void -_cairo_matrix_fini (cairo_matrix_t *matrix); - -cairo_private cairo_status_t -_cairo_matrix_set_translate (cairo_matrix_t *matrix, - double tx, double ty); - -cairo_private cairo_status_t -_cairo_matrix_set_scale (cairo_matrix_t *matrix, - double sx, double sy); - -cairo_private cairo_status_t -_cairo_matrix_set_rotate (cairo_matrix_t *matrix, - double angle); - -cairo_private cairo_status_t -_cairo_matrix_transform_bounding_box (cairo_matrix_t *matrix, +_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, double *x, double *y, double *width, double *height); -cairo_private cairo_status_t -_cairo_matrix_compute_determinant (cairo_matrix_t *matrix, double *det); +cairo_private void +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix, double *det); -cairo_private cairo_status_t -_cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, double *lambda2); +cairo_private void +_cairo_matrix_compute_eigen_values (const cairo_matrix_t *matrix, + double *lambda1, double *lambda2); cairo_private cairo_status_t -_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major); +_cairo_matrix_compute_scale_factors (const cairo_matrix_t *matrix, + double *sx, double *sy, int x_major); cairo_private cairo_bool_t -_cairo_matrix_is_integer_translation(cairo_matrix_t *matrix, int *itx, int *ity); +_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, + int *itx, int *ity); /* cairo_traps.c */ cairo_private void _cairo_traps_init (cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_traps_init_box (cairo_traps_t *traps, + cairo_box_t *box); + cairo_private void _cairo_traps_fini (cairo_traps_t *traps); @@ -1663,6 +1670,10 @@ _cairo_traps_contain (cairo_traps_t *traps, double x, double y); cairo_private void _cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents); +cairo_private cairo_status_t +_cairo_traps_extract_region (cairo_traps_t *tr, + pixman_region16_t **region); + /* cairo_slope.c */ cairo_private void _cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b); @@ -1683,7 +1694,7 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other); cairo_private void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, - double red, double green, double blue); + const cairo_color_t *color); cairo_private void _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, @@ -1702,21 +1713,14 @@ cairo_private void _cairo_pattern_fini (cairo_pattern_t *pattern); cairo_private cairo_pattern_t * -_cairo_pattern_create_solid (double red, double green, double blue); - -cairo_private cairo_status_t -_cairo_pattern_get_rgb (cairo_pattern_t *pattern, - double *red, double *green, double *blue); +_cairo_pattern_create_solid (const cairo_color_t *color); cairo_private void -_cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha); - -cairo_private void -_cairo_pattern_transform (cairo_pattern_t *pattern, - cairo_matrix_t *ctm_inverse); +_cairo_pattern_transform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm_inverse); cairo_private cairo_bool_t -_cairo_pattern_is_opaque (cairo_pattern_t *pattern); +_cairo_pattern_is_opaque_solid (cairo_pattern_t *pattern); cairo_private cairo_int_status_t _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, @@ -1752,37 +1756,71 @@ _cairo_pattern_acquire_surfaces (cairo_pattern_t *src, /* cairo_unicode.c */ cairo_private cairo_status_t -_cairo_utf8_to_ucs4 (const char *str, - int len, - uint32_t **result, - int *items_written); +_cairo_utf8_to_ucs4 (const unsigned char *str, + int len, + uint32_t **result, + int *items_written); cairo_private cairo_status_t -_cairo_utf8_to_utf16 (const char *str, - int len, - uint16_t **result, - int *items_written); +_cairo_utf8_to_utf16 (const unsigned char *str, + int len, + uint16_t **result, + int *items_written); + +/* cairo_output_stream.c */ + +typedef struct _cairo_output_stream cairo_output_stream_t; + +cairo_private cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_func, + void *closure); + +cairo_private void +_cairo_output_stream_destroy (cairo_output_stream_t *stream); + +cairo_private cairo_status_t +_cairo_output_stream_write (cairo_output_stream_t *stream, + const void *data, size_t length); + +cairo_private cairo_status_t +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, va_list ap); + +cairo_private cairo_status_t +_cairo_output_stream_printf (cairo_output_stream_t *stream, + const char *fmt, ...); + +cairo_private long +_cairo_output_stream_get_position (cairo_output_stream_t *status); + +cairo_private cairo_status_t +_cairo_output_stream_get_status (cairo_output_stream_t *stream); + +cairo_output_stream_t * +_cairo_output_stream_create_for_file (const char *filename); /* Avoid unnecessary PLT entries. */ +slim_hidden_proto(cairo_get_current_point) +slim_hidden_proto(cairo_fill_preserve) +slim_hidden_proto(cairo_clip_preserve) slim_hidden_proto(cairo_close_path) -slim_hidden_proto(cairo_matrix_copy) slim_hidden_proto(cairo_matrix_invert) slim_hidden_proto(cairo_matrix_multiply) slim_hidden_proto(cairo_matrix_scale) -slim_hidden_proto(cairo_matrix_set_affine) -slim_hidden_proto(cairo_matrix_set_identity) +slim_hidden_proto(cairo_matrix_init) +slim_hidden_proto(cairo_matrix_init_identity) +slim_hidden_proto(cairo_matrix_init_translate) +slim_hidden_proto(cairo_matrix_init_scale) +slim_hidden_proto(cairo_matrix_init_rotate) slim_hidden_proto(cairo_matrix_transform_distance) slim_hidden_proto(cairo_matrix_transform_point) slim_hidden_proto(cairo_move_to) +slim_hidden_proto(cairo_new_path) slim_hidden_proto(cairo_rel_line_to) slim_hidden_proto(cairo_restore) slim_hidden_proto(cairo_save) -slim_hidden_proto(cairo_set_target_surface) -slim_hidden_proto(cairo_surface_create_for_image) +slim_hidden_proto(cairo_stroke_preserve) slim_hidden_proto(cairo_surface_destroy) -slim_hidden_proto(cairo_surface_get_matrix) -slim_hidden_proto(cairo_surface_set_matrix) -slim_hidden_proto(cairo_surface_set_repeat) #endif |