diff --git a/.gitignore b/.gitignore index 73e1e4e75..f8e50fe9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +ChangeLog Makefile Makefile.in aclocal.m4 diff --git a/AUTHORS b/AUTHORS index e6f0863c3..ad0b97cfb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,6 +14,7 @@ John Ehresman Build fixes for win32 John Ellson First font/glyph extents functions Behdad Esfahbod Release script improvements, bug fixes. Bertram Felgenhauer Fixes for subtle arithmetic errors +Bdale Garbee Provided essential support for cairo achitecture sessions J. Ali Harlow win32 backend updates Richard Henderson "slim" macros for better shared libraries James Henstridge Build fixes related to freetype diff --git a/INSTALL b/INSTALL index 4a49f008d..ab82781bd 100644 --- a/INSTALL +++ b/INSTALL @@ -16,7 +16,7 @@ This final step may require temporary root access (eg. with sudo) if you don't have write permission to the directory in which cairo will be installed. -NOTE: If you are working with source from CVS rather than from a tar +NOTE: If you are working with source from git/cvs rather than from a tar file, then you should use ./autogen.sh in place of ./configure anywhere it is mentioned in these instructions. diff --git a/Makefile.am b/Makefile.am index ab5ca24bf..165f9c349 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,21 @@ EXTRA_DIST = \ COPYING-LGPL-2.1 \ COPYING-MPL-1.1 \ cairo.pc.in +MAINTAINERCLEANFILES = \ + $(srcdir)/INSTALL \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/autoscan.log \ + $(srcdir)/compile \ + $(srcdir)/config.guess \ + $(srcdir)/config.h.in \ + $(srcdir)/config.sub \ + $(srcdir)/configure.scan \ + $(srcdir)/depcomp \ + $(srcdir)/install-sh \ + $(srcdir)/ltmain.sh \ + $(srcdir)/missing \ + $(srcdir)/mkinstalldirs \ + `find "$(srcdir)" -type f -name Makefile.in -print` pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = cairo.pc @@ -20,6 +35,30 @@ check-valgrind: all DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc +# Creating ChangeLog from git log: + +MAINTAINERCLEANFILES += ChangeLog + +EXTRA_DIST += ChangeLog + +ChangeLog: $(srcdir)/ChangeLog + +$(srcdir)/ChangeLog: + @if test -d "$(srcdir)/.git"; then \ + (cd "$(srcdir)" && \ + ./missing --run git-log) | fmt --split-only > $@.tmp \ + && mv -f $@.tmp $@ \ + || ($(RM) $@.tmp; \ + echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \ + (test -f $@ || echo git-log is required to generate this file >> $@)); \ + else \ + test -f $@ || \ + (echo A git checkout and git-log is required to generate ChangeLog >&2 && \ + echo A git checkout and git-log is required to generate this file >> $@); \ + fi + +.PHONY: ChangeLog $(srcdir)/ChangeLog + # Some custom targets to make it easier to release things. # Use either: # make release-check @@ -82,7 +121,7 @@ release-verify-newer: @echo -n "Checking that no $(VERSION) release already exists..." @ssh $(RELEASE_UPLOAD_HOST) test ! -e $(RELEASE_UPLOAD_DIR)/$(tar_file) \ || (echo "Ouch." && echo "Found: $(RELEASE_UPLOAD_HOST):$(RELEASE_UPLOAD_DIR)/$(tar_file)" \ - && echo "Are you sure you have an updated CVS checkout?" \ + && echo "Are you sure you have an updated checkout?" \ && echo "This should never happen." \ && false) @echo "Good." @@ -104,9 +143,9 @@ release-upload: release-check $(tar_file) $(sha1_file) $(gpg_file) scp $(tar_file) $(sha1_file) $(gpg_file) $(RELEASE_UPLOAD_HOST):$(RELEASE_UPLOAD_DIR) mv $(tar_file) $(sha1_file) $(gpg_file) releases ssh $(RELEASE_UPLOAD_HOST) "rm -f $(RELEASE_UPLOAD_DIR)/LATEST-$(PACKAGE)-[0-9]* && ln -s $(tar_file) $(RELEASE_UPLOAD_DIR)/LATEST-$(PACKAGE)-$(VERSION)" - $(CVS) tag RELEASE_$(CAIRO_VERSION_MAJOR)_$(CAIRO_VERSION_MINOR)_$(CAIRO_VERSION_MICRO) + git tag -s $(CAIRO_VERSION_MAJOR).$(CAIRO_VERSION_MINOR).$(CAIRO_VERSION_MICRO) -release-publish: release-upload releases/$(sha1_file) +release-publish-message: releases/$(sha1_file) @echo "" @echo "Please send an announcement to $(RELEASE_ANNOUNCE_LIST)" @echo "including the following:" @@ -133,6 +172,8 @@ release-publish: release-upload releases/$(sha1_file) @echo "Last but not least, do not forget to bump up the micro" @echo "version component to the next (odd) number and commit." +release-publish: release-upload release-publish-message + # XXX: Depending on all here is rather overkill. We don't really need # the library built in order to create the documentation. docs-publish: all diff --git a/RELEASING b/RELEASING index df87e3847..49dd82ce9 100644 --- a/RELEASING +++ b/RELEASING @@ -1,12 +1,7 @@ Here are the steps to follow to create a new cairo release: -1) Ensure that there are no local, uncommitted modifications. The best - thing to do here may be to begin with a fresh checkout from CVS: - - cvs -d cairographics.org:/cvs/cairo co cairo - - But it's probably good enough if "cvs -q update -Ad" generates no - output. +1) Ensure that there are no local, uncommitted modifications. + It's probably good enough if "git diff" doesn't output anything. 2) Verify that the code passes "make distcheck" @@ -32,7 +27,10 @@ Here are the steps to follow to create a new cairo release: previous release tag: find src/ -name '*.h' -not -name '*-private.h' -not -name 'cairoint.h' | \ - xargs cvs diff -r RELEASE_X_Y_Z + xargs git diff X.Y.Z -- + + Note that for older releases made under CVS, the tag name is + RELEASE_X_Y_Z instead. 4) Increment cairo_version_{minor|micro} and LT_{CURRENT|VERSION|AGE} in configure.in: @@ -55,9 +53,8 @@ Here are the steps to follow to create a new cairo release: 5) Commit the changes to NEWS and configure.in - Don't forget to fill out the ChangeLog just like with any - other commit. It's especially important to mention the new - version number in the ChangeLog. + It's especially important to mention the new version number in your + commit log. 6) Run "make release-publish" which will perform the following steps for you: @@ -71,8 +68,13 @@ Here are the steps to follow to create a new cairo release: * scp the three files to appear on http://cairographics.org/releases * Place local copies of the three files in the releases directory * Create a LATEST-package-version file (after deleting any old one) - * Tag the entire source tree with a tag of the form RELEASE_X_Y_Z + * Tag the entire source tree with a tag of the form X.Y.Z, and sign + the tag with your GPG key (asks for your GPG password, and you + may need to set GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL to match + your public-key's setting or this fails.) * Provide some text for the release announcement (see below). + If for some reason you lost this message, "make release-publish-message" + prints it for you. 7) Increment cairo_version_micro to the next larger (odd) number in configure, and commit. diff --git a/autogen.sh b/autogen.sh index 88ad4f74b..57bf60111 100755 --- a/autogen.sh +++ b/autogen.sh @@ -7,7 +7,7 @@ PACKAGE=cairo LIBTOOLIZE=${LIBTOOLIZE-libtoolize} LIBTOOLIZE_FLAGS="--copy --force" AUTOHEADER=${AUTOHEADER-autoheader} -AUTOMAKE_FLAGS="--add-missing" +AUTOMAKE_FLAGS="--add-missing --foreign" AUTOCONF=${AUTOCONF-autoconf} # automake 1.8 requires autoconf 2.58 diff --git a/configure.in b/configure.in index b957ada3d..020b394d1 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ AC_PREREQ(2.54) # cairo package version number, (as distinct from shared library version) -# An odd micro number indicates in-progress development, (eg. from CVS) +# An odd micro number indicates in-progress development, (eg. from git/cvs) # An even micro number indicates a released version. m4_define(cairo_version_major, 1) m4_define(cairo_version_minor, 1) @@ -810,6 +810,6 @@ if test x"$use_beos" = "xyes" ; then echo "$WARNING_MESSAGE" | sed 's/@BACKEND@/BeOS/' fi -if test x"$use_directfb" == "xyes" ; then +if test x"$use_directfb" = "xyes" ; then echo "$WARNING_MESSAGE" | sed 's/@BACKEND@/DirectFB/' fi diff --git a/doc/public/cairo-docs.xml b/doc/public/cairo-docs.xml index 679eb9202..b78d269f4 100644 --- a/doc/public/cairo-docs.xml +++ b/doc/public/cairo-docs.xml @@ -40,6 +40,7 @@ + +SVG Surfaces + + +Rendering SVG documents + + + + + + + + + + + + + + + + + + + +@filename: +@width_in_points: +@height_in_points: +@Returns: + + + + + + + +@write_func: +@closure: +@width_in_points: +@height_in_points: +@Returns: + + + + + + + +@surface: +@x_dpi: +@y_dpi: + + diff --git a/pixman/src/fbcompose.c b/pixman/src/fbcompose.c index 2289cee09..e316c6db6 100644 --- a/pixman/src/fbcompose.c +++ b/pixman/src/fbcompose.c @@ -33,10 +33,18 @@ #include "pixregionint.h" +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif + #include -// #define PIXMAN_CONVOLUTION -// #define PIXMAN_INDEXED_FORMATS +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* #define PIXMAN_CONVOLUTION */ +/* #define PIXMAN_INDEXED_FORMATS */ static Bool PictureTransformPoint3d (pixman_transform_t *transform, diff --git a/pixman/src/fbedge.c b/pixman/src/fbedge.c index 2ee6e6cdc..f1289b3fa 100644 --- a/pixman/src/fbedge.c +++ b/pixman/src/fbedge.c @@ -1,6 +1,4 @@ /* - * $Id: fbedge.c,v 1.3 2005-08-02 01:01:24 vektor Exp $ - * * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its diff --git a/pixman/src/fbedgeimp.h b/pixman/src/fbedgeimp.h index 87691faf2..aa9ca90fa 100644 --- a/pixman/src/fbedgeimp.h +++ b/pixman/src/fbedgeimp.h @@ -1,6 +1,4 @@ /* - * $Id: fbedgeimp.h,v 1.2 2005-01-21 18:38:42 cworth Exp $ - * * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its diff --git a/pixman/src/fbpict.c b/pixman/src/fbpict.c index e074174e7..a09174009 100644 --- a/pixman/src/fbpict.c +++ b/pixman/src/fbpict.c @@ -1,6 +1,4 @@ /* - * $Id: fbpict.c,v 1.8 2006-01-21 17:39:11 biesi Exp $ - * * Copyright © 2000 SuSE, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -1151,7 +1149,7 @@ fbCompositeTrans_0888xnx0888(pixman_operator_t op, setupPackedReader(ws,wt,isrc,wsrc,workingSource); /* get to word aligned */ - switch(!(long)dst&3) + switch(~(long)dst&3) { case 1: readPackedSource(rs); @@ -1227,7 +1225,7 @@ fbCompositeTrans_0888xnx0888(pixman_operator_t op, srcLine += srcStride; w = width*3; /* get to word aligned */ - switch(!(long)src&3) + switch(~(long)src&3) { case 1: rd=alphamaskCombine24(*src++, *dst)>>8; diff --git a/pixman/src/fbpict.h b/pixman/src/fbpict.h index 95742ed8b..fbf00ed77 100644 --- a/pixman/src/fbpict.h +++ b/pixman/src/fbpict.h @@ -1,6 +1,4 @@ /* - * $Id: fbpict.h,v 1.2 2005-09-12 12:55:11 otaylor Exp $ - * * Copyright © 2000 Keith Packard * 2005 Lars Knoll & Zack Rusin, Trolltech * diff --git a/pixman/src/icpixels.c b/pixman/src/icpixels.c index 7b3798ed8..2ad34f478 100644 --- a/pixman/src/icpixels.c +++ b/pixman/src/icpixels.c @@ -1,6 +1,4 @@ /* - * $Id: icpixels.c,v 1.9 2005-06-25 03:13:19 jrmuizel Exp $ - * * Copyright © 1998 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its @@ -72,6 +70,7 @@ FbPixelsCreate (int width, int height, int depth) return NULL; buf = (pixman_bits_t *) ((char *)pixels + base + adjust); + memset (buf, 0, height * stride); FbPixelsInit (pixels, buf, width, height, depth, bpp, stride); diff --git a/pixman/src/ictrap.c b/pixman/src/ictrap.c index 0ac51ca09..08bd0247b 100644 --- a/pixman/src/ictrap.c +++ b/pixman/src/ictrap.c @@ -1,6 +1,4 @@ /* - * $Id: ictrap.c,v 1.27 2005-08-28 02:32:57 vektor Exp $ - * * Copyright © 2002 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its diff --git a/pixman/src/pixman-xserver-compat.h b/pixman/src/pixman-xserver-compat.h index 46e2825f9..5113abd54 100644 --- a/pixman/src/pixman-xserver-compat.h +++ b/pixman/src/pixman-xserver-compat.h @@ -72,8 +72,8 @@ typedef pixman_triangle_t xTriangle; #define FB_SHIFT IC_SHIFT #define FB_MASK IC_MASK #define FB_ALLONES IC_ALLONES +#define FbMaskBits IcMaskBits */ -//#define FbMaskBits IcMaskBits /* XXX: We changed some function and field names which makes for some * ugly hacks... */ diff --git a/pixman/src/pixman.h b/pixman/src/pixman.h index bd5194eb1..00884e7ce 100644 --- a/pixman/src/pixman.h +++ b/pixman/src/pixman.h @@ -54,8 +54,6 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ -/* $Id: pixman.h,v 1.25 2006-01-05 00:26:10 cworth Exp $ */ - /* libic.h */ /* @@ -83,7 +81,7 @@ SOFTWARE. #if defined (__SVR4) && defined (__sun) # include -#elif defined (__OpenBSD__) || defined (_AIX) +#elif defined (__OpenBSD__) || defined (_AIX) || defined (__osf__) # include #elif defined (_MSC_VER) typedef __int8 int8_t; diff --git a/pixman/src/pixregion.c b/pixman/src/pixregion.c index 9c122f534..e5660969e 100644 --- a/pixman/src/pixregion.c +++ b/pixman/src/pixregion.c @@ -60,7 +60,7 @@ SOFTWARE. #endif #undef assert -#ifdef DEBUG +#ifdef DEBUG_PIXREGION #define assert(expr) {if (!(expr)) \ FatalError("Assertion failed file %s, line %d: expr\n", \ __FILE__, __LINE__); } @@ -208,7 +208,7 @@ if (((numRects) < ((reg)->data->size >> 1)) && ((reg)->data->size > 50)) \ } -#ifdef DEBUG +#ifdef DEBUG_PIXREGION int pixman_region16_print(rgn) pixman_region16_t * rgn; @@ -302,7 +302,7 @@ pixman_region16_valid(reg) } } -#endif /* DEBUG */ +#endif /* DEBUG_PIXREGION */ /* Create a new empty region */ diff --git a/pixman/src/pixregionint.h b/pixman/src/pixregionint.h index 86ff8e655..e47c45590 100644 --- a/pixman/src/pixregionint.h +++ b/pixman/src/pixregionint.h @@ -44,8 +44,6 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ -/* $Id: pixregionint.h,v 1.7 2004-04-16 15:32:53 cworth Exp $ */ - #ifndef _PIXREGIONINT_H_ #define _PIXREGIONINT_H_ diff --git a/pixman/src/renderedge.c b/pixman/src/renderedge.c index 525ea73f2..56fcfb3e0 100644 --- a/pixman/src/renderedge.c +++ b/pixman/src/renderedge.c @@ -1,6 +1,4 @@ /* - * $Id: renderedge.c,v 1.2 2005-01-21 18:26:28 cworth Exp $ - * * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its diff --git a/pixman/src/renderedge.h b/pixman/src/renderedge.h index 5c7c1e540..522e5034e 100644 --- a/pixman/src/renderedge.h +++ b/pixman/src/renderedge.h @@ -1,6 +1,4 @@ /* - * $Id: renderedge.h,v 1.3 2005-02-21 21:29:22 cworth Exp $ - * * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its diff --git a/src/Makefile.am b/src/Makefile.am index 908bcb284..4ccf3278b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -56,6 +56,11 @@ libcairo_beos_sources = if CAIRO_HAS_BEOS_SURFACE libcairo_beos_headers = cairo-beos.h libcairo_beos_sources += cairo-beos-surface.cpp + +noinst_LTLIBRARIES = libcairo_beos.la +libcairo_beos_la_SOURCES = $(libcairo_beos_sources) +# BeOS system headers trigger this warning +libcairo_beos_la_CXXFLAGS = -Wno-multichar endif if CAIRO_HAS_GLITZ_SURFACE @@ -129,6 +134,7 @@ libcairo_la_SOURCES = \ cairo-arc.c \ cairo-arc-private.h \ cairo-array.c \ + cairo-base85-stream.c \ cairo-cache.c \ cairo-cache-private.h \ cairo-clip.c \ @@ -145,7 +151,9 @@ libcairo_la_SOURCES = \ cairo-hash-private.h \ cairo-hull.c \ cairo-image-surface.c \ + cairo-lzw.c \ cairo-matrix.c \ + cairo-operator.c \ cairo-path.c \ cairo-path-bounds.c \ cairo-path-data.c \ @@ -173,6 +181,8 @@ libcairo_la_SOURCES = \ cairo-meta-surface-private.h \ cairo-paginated-surface.c \ cairo-paginated-surface-private.h \ + cairo-analysis-surface.c \ + cairo-analysis-surface-private.h \ $(libcairo_atsui_sources) \ $(libcairo_ft_sources) \ $(libcairo_ps_sources) \ @@ -187,19 +197,16 @@ libcairo_la_SOURCES = \ $(libcairo_glitz_sources) \ $(libcairo_win32_sources) \ $(libcairo_beos_sources) \ - $(libcairo_directfb_sources) \ + $(libcairo_directfb_sources) \ cairoint.h libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined $(export_symbols) -# this -Wno-multichar line is really just for the beos surface, because the -# system headers trigger this warning. -libcairo_la_CXXFLAGS = -Wno-multichar INCLUDES = -I$(srcdir) -I$(top_srcdir)/pixman/src $(CAIRO_CFLAGS) -libcairo_la_LIBADD = $(top_builddir)/pixman/src/libpixman.la $(CAIRO_LIBS) +libcairo_la_LIBADD = $(top_builddir)/pixman/src/libpixman.la $(CAIRO_LIBS) $(noinst_LTLIBRARIES) -libcairo_la_DEPENDENCIES = $(cairo_def_dependency) $(top_builddir)/pixman/src/libpixman.la +libcairo_la_DEPENDENCIES = $(cairo_def_dependency) $(top_builddir)/pixman/src/libpixman.la $(noinst_LTLIBRARIES) EXTRA_DIST = \ cairo.def diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h new file mode 100644 index 000000000..62e67ae68 --- /dev/null +++ b/src/cairo-analysis-surface-private.h @@ -0,0 +1,55 @@ +/* $Id: $ + * + * Copyright © 2005 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 Packard + */ + +#ifndef CAIRO_ANALYSIS_SURFACE_H +#define CAIRO_ANALYSIS_SURFACE_H + +#include "cairoint.h" + +cairo_private cairo_surface_t * +_cairo_analysis_surface_create (cairo_surface_t *target, + int width, + int height); + +cairo_private pixman_region16_t * +_cairo_analysis_surface_get_supported (cairo_surface_t *surface); + +cairo_private pixman_region16_t * +_cairo_analysis_surface_get_unsupported (cairo_surface_t *unsupported); + +cairo_private cairo_bool_t +_cairo_analysis_surface_has_unsupported (cairo_surface_t *unsupported); + +#endif /* CAIRO_ANALYSIS_SURFACE_H */ diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c new file mode 100644 index 000000000..76b84e10b --- /dev/null +++ b/src/cairo-analysis-surface.c @@ -0,0 +1,256 @@ +/* + * Copyright © 2006 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 Packard + */ + +#include "cairoint.h" + +#include "cairo-analysis-surface-private.h" +#include "cairo-paginated-surface-private.h" + +typedef struct { + cairo_surface_t base; + int width; + int height; + + cairo_surface_t *target; + + cairo_bool_t fallback; +} cairo_analysis_surface_t; + +static cairo_int_status_t +_cairo_analysis_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + cairo_analysis_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static cairo_int_status_t +_cairo_analysis_surface_paint (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (!surface->target->backend->paint) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + status = (*surface->target->backend->paint) (surface->target, op, + source); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + surface->fallback = TRUE; + status = CAIRO_STATUS_SUCCESS; + } + return status; +} + +static cairo_int_status_t +_cairo_analysis_surface_mask (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_pattern_t *mask) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (!surface->target->backend->mask) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + status = (*surface->target->backend->mask) (surface->target, op, + source, mask); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + surface->fallback = TRUE; + status = CAIRO_STATUS_SUCCESS; + } + return status; +} + +static cairo_int_status_t +_cairo_analysis_surface_stroke (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (!surface->target->backend->stroke) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + status = (*surface->target->backend->stroke) (surface->target, op, + source, path, style, + ctm, ctm_inverse, + tolerance, antialias); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + surface->fallback = TRUE; + status = CAIRO_STATUS_SUCCESS; + } + return status; +} + +static cairo_int_status_t +_cairo_analysis_surface_fill (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (!surface->target->backend->fill) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + status = (*surface->target->backend->fill) (surface->target, op, + source, path, fill_rule, + tolerance, antialias); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + surface->fallback = TRUE; + status = CAIRO_STATUS_SUCCESS; + } + return status; +} + +static cairo_int_status_t +_cairo_analysis_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (!surface->target->backend->show_glyphs) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + status = (*surface->target->backend->show_glyphs) (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + surface->fallback = TRUE; + status = CAIRO_STATUS_SUCCESS; + } + return status; +} + +static const cairo_surface_backend_t cairo_analysis_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, + NULL, /* create_similar */ + NULL, /* finish_surface */ + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* copy_page */ + NULL, /* show_page */ + NULL, /* set_clip_region */ + NULL, /* clip_path */ + _cairo_analysis_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + _cairo_analysis_surface_paint, + _cairo_analysis_surface_mask, + _cairo_analysis_surface_stroke, + _cairo_analysis_surface_fill, + _cairo_analysis_surface_show_glyphs, + NULL, /* snapshot */ +}; + +cairo_private cairo_surface_t * +_cairo_analysis_surface_create (cairo_surface_t *target, + int width, + int height) +{ + cairo_analysis_surface_t *surface; + + surface = malloc (sizeof (cairo_analysis_surface_t)); + if (surface == NULL) + goto FAIL; + + _cairo_surface_init (&surface->base, &cairo_analysis_surface_backend); + + surface->width = width; + surface->height = height; + + surface->target = target; + surface->fallback = FALSE; + + return &surface->base; +FAIL: + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return NULL; +} + +cairo_private pixman_region16_t * +_cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface) +{ + /* XXX */ + return NULL; +} + +cairo_private pixman_region16_t * +_cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface) +{ + /* XXX */ + return NULL; +} + +cairo_private cairo_bool_t +_cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->fallback; +} + + diff --git a/src/cairo-atsui-font.c b/src/cairo-atsui-font.c index 9f3b54da1..76b5a3c86 100644 --- a/src/cairo-atsui-font.c +++ b/src/cairo-atsui-font.c @@ -40,6 +40,16 @@ #include "cairo.h" #include "cairo-quartz-private.h" +/* + * FixedToFloat/FloatToFixed are 10.3+ SDK items - include definitions + * here so we can use older SDKs. + */ +#ifndef FixedToFloat +#define fixed1 ((Fixed) 0x00010000L) +#define FixedToFloat(a) ((float)(a) / fixed1) +#define FloatToFixed(a) ((Fixed)((float)(a) * fixed1)) +#endif + typedef struct _cairo_atsui_font_face cairo_atsui_font_face_t; typedef struct _cairo_atsui_font cairo_atsui_font_t; @@ -94,6 +104,7 @@ _cairo_atsui_font_face_scaled_font_create (void *abstract_face, } static const cairo_font_face_backend_t _cairo_atsui_font_face_backend = { + CAIRO_FONT_TYPE_ATSUI, _cairo_atsui_font_face_destroy, _cairo_atsui_font_face_scaled_font_create }; @@ -132,7 +143,7 @@ CreateSizedCopyOfStyle(ATSUStyle inStyle, const cairo_matrix_t *scale) ATSUStyle style; OSStatus err; - // Set the style's size + /* Set the style's size */ CGAffineTransform theTransform = CGAffineTransformMakeWithCairoFontScale(scale); Fixed theSize = @@ -174,7 +185,7 @@ _cairo_atsui_font_set_metrics (cairo_atsui_font_t *font) 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. + /* The FT backend doesn't handle max_y_advance either, so we'll ignore it for now. */ extents.max_y_advance = 0.0; _cairo_scaled_font_set_metrics (&font->base, &extents); @@ -275,7 +286,7 @@ _cairo_atsui_font_create_toy(cairo_toy_font_face_t *toy_face, kFontNoLanguageCode, &fontID); if (err != noErr) { - // couldn't get the font - remap css names and try again + /* couldn't get the font - remap css names and try again */ if (!strcmp(family, "serif")) family = "Times"; @@ -287,7 +298,7 @@ _cairo_atsui_font_create_toy(cairo_toy_font_face_t *toy_face, family = "Gadget"; else if (!strcmp(family, "monospace")) family = "Courier"; - else // anything else - return error instead? + else /* anything else - return error instead? */ family = "Courier"; err = ATSUFindFontFromName(family, strlen(family), @@ -504,7 +515,7 @@ _cairo_atsui_font_text_to_glyphs (void *abstract_font, err = ATSUSetTextPointerLocation(textLayout, utf16, 0, n16, n16); - // Set the style for all of the text + /* Set the style for all of the text */ err = ATSUSetRunStyle(textLayout, font->style, kATSUFromTextBeginning, kATSUToTextEnd); @@ -552,117 +563,140 @@ _cairo_atsui_font_old_show_glyphs (void *abstract_font, int num_glyphs) { cairo_atsui_font_t *font = abstract_font; - CGContextRef myBitmapContext; - CGColorSpaceRef colorSpace; + CGContextRef myBitmapContext = 0, drawingContext; + CGColorSpaceRef colorSpace = 0;; cairo_image_surface_t *destImageSurface; int i; void *extra = NULL; + cairo_bool_t can_draw_directly; + cairo_rectangle_t rect; - cairo_rectangle_t rect = {dest_x, dest_y, width, height}; - _cairo_surface_acquire_dest_image(generic_surface, - &rect, - &destImageSurface, - &rect, - &extra); + /* Check if we can draw directly to the destination surface */ + can_draw_directly = _cairo_surface_is_quartz (generic_surface) && + _cairo_pattern_is_opaque_solid (pattern) && + op == CAIRO_OPERATOR_OVER; - // Create a CGBitmapContext for the dest surface for drawing into - colorSpace = CGColorSpaceCreateDeviceRGB(); + if (!can_draw_directly) { + rect.x = dest_x; + rect.y = dest_y; + rect.width = width; + rect.height = height; - 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); + _cairo_surface_acquire_dest_image(generic_surface, + &rect, + &destImageSurface, + &rect, + &extra); + + /* 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); + CGContextTranslateCTM(myBitmapContext, 0, destImageSurface->height); + CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f); + + drawingContext = myBitmapContext; + } else { + drawingContext = ((cairo_quartz_surface_t *)generic_surface)->context; + CGContextSaveGState (drawingContext); + } ATSFontRef atsFont = FMGetATSFontRefFromFont(font->fontID); CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFont); - CGContextSetFont(myBitmapContext, cgFont); + CGContextSetFont(drawingContext, cgFont); CGAffineTransform textTransform = CGAffineTransformMakeWithCairoFontScale(&font->base.scale); textTransform = CGAffineTransformScale(textTransform, 1.0f, -1.0f); - CGContextSetFontSize(myBitmapContext, 1.0); - CGContextSetTextMatrix(myBitmapContext, textTransform); + CGContextSetFontSize(drawingContext, 1.0); + CGContextSetTextMatrix(drawingContext, textTransform); - if (pattern->type == CAIRO_PATTERN_SOLID && + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID && _cairo_pattern_is_opaque_solid(pattern)) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *)pattern; - CGContextSetRGBFillColor(myBitmapContext, + CGContextSetRGBFillColor(drawingContext, solid->color.red, solid->color.green, solid->color.blue, 1.0f); } else { - CGContextSetRGBFillColor(myBitmapContext, 0.0f, 0.0f, 0.0f, 0.0f); + CGContextSetRGBFillColor(drawingContext, 0.0f, 0.0f, 0.0f, 0.0f); } - if (_cairo_surface_is_quartz (generic_surface)) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)generic_surface; - if (surface->clip_region) { - pixman_box16_t *boxes = pixman_region_rects (surface->clip_region); - int num_boxes = pixman_region_num_rects (surface->clip_region); - CGRect stack_rects[10]; - CGRect *rects; - int i; - - if (num_boxes > 10) - rects = malloc (sizeof (CGRect) * num_boxes); - else - rects = stack_rects; - - for (i = 0; i < num_boxes; i++) { - rects[i].origin.x = boxes[i].x1; - rects[i].origin.y = boxes[i].y1; - rects[i].size.width = boxes[i].x2 - boxes[i].x1; - rects[i].size.height = boxes[i].y2 - boxes[i].y1; - } - - CGContextClipToRects (myBitmapContext, rects, num_boxes); - - if (rects != stack_rects) - free(rects); - } - } else { - /* XXX: Need to get the text clipped */ + if (_cairo_surface_is_quartz (generic_surface)) { + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)generic_surface; + if (surface->clip_region) { + pixman_box16_t *boxes = pixman_region_rects (surface->clip_region); + int num_boxes = pixman_region_num_rects (surface->clip_region); + CGRect stack_rects[10]; + CGRect *rects; + int i; + + if (num_boxes > 10) + rects = malloc (sizeof (CGRect) * num_boxes); + else + rects = stack_rects; + + for (i = 0; i < num_boxes; i++) { + rects[i].origin.x = boxes[i].x1; + rects[i].origin.y = boxes[i].y1; + rects[i].size.width = boxes[i].x2 - boxes[i].x1; + rects[i].size.height = boxes[i].y2 - boxes[i].y1; + } + + CGContextClipToRects (drawingContext, rects, num_boxes); + + if (rects != stack_rects) + free(rects); } + } else { + /* XXX: Need to get the text clipped */ + } - // 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... + /* 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, + CGContextShowGlyphsAtPoint(drawingContext, glyphs[i].x, glyphs[i].y, &theGlyph, 1); } - - CGColorSpaceRelease(colorSpace); - CGContextRelease(myBitmapContext); - - _cairo_surface_release_dest_image(generic_surface, - &rect, - destImageSurface, - &rect, - extra); + if (!can_draw_directly) { + CGColorSpaceRelease(colorSpace); + CGContextRelease(myBitmapContext); + + _cairo_surface_release_dest_image(generic_surface, + &rect, + destImageSurface, + &rect, + extra); + } else { + CGContextRestoreGState (drawingContext); + } return CAIRO_STATUS_SUCCESS; } const cairo_scaled_font_backend_t cairo_atsui_scaled_font_backend = { + CAIRO_FONT_TYPE_ATSUI, _cairo_atsui_font_create_toy, _cairo_atsui_font_fini, _cairo_atsui_font_scaled_glyph_init, diff --git a/src/cairo-base85-stream.c b/src/cairo-base85-stream.c new file mode 100644 index 000000000..1d4535a9f --- /dev/null +++ b/src/cairo-base85-stream.c @@ -0,0 +1,130 @@ +/* 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 + */ + +#include "cairoint.h" + +typedef struct _cairo_base85_stream { + cairo_output_stream_t *output; + unsigned char four_tuple[4]; + int pending; +} cairo_base85_stream_t; + +static void +_expand_four_tuple_to_five (unsigned char four_tuple[4], + unsigned char five_tuple[5], + cairo_bool_t *all_zero) +{ + uint32_t value; + int digit, i; + + value = four_tuple[0] << 24 | four_tuple[1] << 16 | four_tuple[2] << 8 | four_tuple[3]; + if (all_zero) + *all_zero = TRUE; + for (i = 0; i < 5; i++) { + digit = value % 85; + if (digit != 0 && all_zero) + *all_zero = FALSE; + five_tuple[4-i] = digit + 33; + value = value / 85; + } +} + +static cairo_status_t +_cairo_base85_stream_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + cairo_base85_stream_t *stream = closure; + const unsigned char *ptr = data; + unsigned char five_tuple[5]; + cairo_bool_t is_zero; + + while (length) { + stream->four_tuple[stream->pending++] = *ptr++; + length--; + if (stream->pending == 4) { + _expand_four_tuple_to_five (stream->four_tuple, five_tuple, &is_zero); + if (is_zero) + _cairo_output_stream_write (stream->output, "z", 1); + else + _cairo_output_stream_write (stream->output, five_tuple, 5); + stream->pending = 0; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_cairo_base85_stream_close (void *closure) +{ + cairo_status_t status; + cairo_base85_stream_t *stream = closure; + unsigned char five_tuple[5]; + + if (stream->pending) { + memset (stream->four_tuple + stream->pending, 0, 4 - stream->pending); + _expand_four_tuple_to_five (stream->four_tuple, five_tuple, NULL); + _cairo_output_stream_write (stream->output, five_tuple, stream->pending + 1); + } + + /* Mark end of base85 data */ + _cairo_output_stream_printf (stream->output, "~>"); + + status = _cairo_output_stream_get_status (stream->output); + + free (stream); + + return status; +} + +cairo_output_stream_t * +_cairo_base85_stream_create (cairo_output_stream_t *output) +{ + cairo_base85_stream_t *stream; + + stream = malloc (sizeof (cairo_base85_stream_t)); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; + + stream->output = output; + stream->pending = 0; + + return _cairo_output_stream_create (_cairo_base85_stream_write, + _cairo_base85_stream_close, + stream); +} + diff --git a/src/cairo-beos-surface.cpp b/src/cairo-beos-surface.cpp index b6448fa69..312bd88ff 100644 --- a/src/cairo-beos-surface.cpp +++ b/src/cairo-beos-surface.cpp @@ -70,6 +70,9 @@ struct cairo_beos_surface_t { BBitmap* bitmap; + // If true, surface and view should be deleted when this surface is + // destroyed + bool owns_bitmap_view; }; class AutoLockView { @@ -92,6 +95,11 @@ class AutoLockView { bool mOK; }; +static cairo_surface_t * +_cairo_beos_surface_create_internal (BView* view, + BBitmap* bmp, + bool owns_bitmap_view = false); + static BRect _cairo_rect_to_brect (const cairo_rectangle_t* rect) { @@ -359,6 +367,7 @@ _cairo_image_surface_to_bitmap (cairo_image_surface_t* surface) return data; } default: + assert(0); return NULL; } } @@ -414,10 +423,76 @@ _cairo_op_to_be_op (cairo_operator_t cairo_op, }; } +static cairo_surface_t * +_cairo_beos_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + fprintf(stderr, "Creating similar\n"); + + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + + if (width <= 0) + width = 1; + if (height <= 0) + height = 1; + + BRect rect(0.0, 0.0, width - 1, height - 1); + BBitmap* bmp; + switch (content) { + case CAIRO_CONTENT_ALPHA: + // Can't support this natively + return _cairo_image_surface_create_with_content(content, width, + height); + case CAIRO_CONTENT_COLOR_ALPHA: + bmp = new BBitmap(rect, B_RGBA32, true); + break; + case CAIRO_CONTENT_COLOR: + // Match the color depth + if (surface->bitmap) { + color_space space = surface->bitmap->ColorSpace(); + // No alpha was requested -> make sure not to return + // a surface with alpha + if (space == B_RGBA32) + space = B_RGB32; + if (space == B_RGBA15) + space = B_RGB15; + bmp = new BBitmap(rect, space, true); + } else { + BScreen scr(surface->view->Window()); + color_space space = B_RGB32; + if (scr.IsValid()) + space = scr.ColorSpace(); + bmp = new BBitmap(rect, space, true); + } + break; + default: + assert(0); + return NULL; + + }; + BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0); + bmp->AddChild(view); + return _cairo_beos_surface_create_internal(view, bmp, true); +} + static cairo_status_t _cairo_beos_surface_finish (void *abstract_surface) { - // Nothing to do + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + if (surface->owns_bitmap_view) { + if (surface->bitmap) + surface->bitmap->RemoveChild(surface->view); + + delete surface->view; + delete surface->bitmap; + + surface->view = NULL; + surface->bitmap = NULL; + } return CAIRO_STATUS_SUCCESS; } @@ -549,13 +624,12 @@ _cairo_beos_surface_release_dest_image (void *abstract_surface, cairo_beos_surface_t *surface = reinterpret_cast( abstract_surface); + AutoLockView locker(surface->view); if (!locker) return; - BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image); - surface->view->PushState(); surface->view->SetDrawingMode(B_OP_COPY); @@ -570,18 +644,18 @@ _cairo_beos_surface_release_dest_image (void *abstract_surface, } static cairo_int_status_t -_cairo_beos_composite (cairo_operator_t op, - 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) +_cairo_beos_surface_composite (cairo_operator_t op, + 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) { cairo_beos_surface_t *surface = reinterpret_cast( dst); @@ -598,7 +672,7 @@ _cairo_beos_composite (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; // XXX should eventually support the others - if (src->type != CAIRO_PATTERN_SURFACE || + if (src->type != CAIRO_PATTERN_TYPE_SURFACE || src->extend != CAIRO_EXTEND_NONE) { return CAIRO_INT_STATUS_UNSUPPORTED; @@ -617,63 +691,95 @@ _cairo_beos_composite (cairo_operator_t op, cairo_surface_t* src_surface = reinterpret_cast(src)-> surface; - if (_cairo_surface_is_image(src_surface)) { - fprintf(stderr, "Composite\n"); - // Draw it on screen. + // Get a bitmap + BBitmap* bmp = NULL; + bool free_bmp = false; + if (_cairo_surface_is_image(src_surface)) { cairo_image_surface_t* img_surface = reinterpret_cast(src_surface); - BBitmap* bmp = _cairo_image_surface_to_bitmap(img_surface); - surface->view->PushState(); - - // If our image rect is only a subrect of the desired size, and we - // aren't using B_OP_ALPHA, then we need to fill the rect first. - if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) { - rgb_color black = { 0, 0, 0, 0 }; - - surface->view->SetDrawingMode(mode); - surface->view->SetHighColor(black); - surface->view->FillRect(dstRect); - } - - if (mode == B_OP_ALPHA && img_surface->format != CAIRO_FORMAT_ARGB32) { - mode = B_OP_COPY; - - } - surface->view->SetDrawingMode(mode); - - if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) - surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); - else - surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); - - surface->view->DrawBitmap(bmp, srcRect, dstRect); - - surface->view->PopState(); - delete bmp; - - return CAIRO_INT_STATUS_SUCCESS; + bmp = _cairo_image_surface_to_bitmap(img_surface); + free_bmp = true; + } else if (src_surface->backend == surface->base.backend) { + cairo_beos_surface_t *beos_surface = + reinterpret_cast(src_surface); + if (beos_surface->bitmap) { + AutoLockView locker(beos_surface->view); + if (locker) + beos_surface->view->Sync(); + bmp = beos_surface->bitmap; + } else { + _cairo_beos_view_to_bitmap(surface->view, &bmp); + free_bmp = true; + } } - return CAIRO_INT_STATUS_UNSUPPORTED; + if (!bmp) + return CAIRO_INT_STATUS_UNSUPPORTED; + + // So, BeOS seems to screw up painting an opaque bitmap onto a + // translucent one (it makes them partly transparent). Just return + // unsupported. + if (bmp->ColorSpace() == B_RGB32 && surface->bitmap && + surface->bitmap->ColorSpace() == B_RGBA32 && + (mode == B_OP_COPY || mode == B_OP_ALPHA)) + { + if (free_bmp) + delete bmp; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + fprintf(stderr, "Composite\n"); + + // Draw it on screen. + surface->view->PushState(); + + // If our image rect is only a subrect of the desired size, and we + // aren't using B_OP_ALPHA, then we need to fill the rect first. + if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) { + rgb_color black = { 0, 0, 0, 0 }; + + surface->view->SetDrawingMode(mode); + surface->view->SetHighColor(black); + surface->view->FillRect(dstRect); + } + + if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) { + mode = B_OP_COPY; + } + surface->view->SetDrawingMode(mode); + + if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) + surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + else + surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + + surface->view->DrawBitmap(bmp, srcRect, dstRect); + + surface->view->PopState(); + + if (free_bmp) + delete bmp; + + return CAIRO_INT_STATUS_SUCCESS; } static void -_cairo_beos_fill_rectangle (cairo_beos_surface_t *surface, - cairo_rectangle_t *rect) +_cairo_beos_surface_fill_rectangle (cairo_beos_surface_t *surface, + cairo_rectangle_t *rect) { BRect brect(_cairo_rect_to_brect(rect)); surface->view->FillRect(brect); } static cairo_int_status_t -_cairo_beos_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) +_cairo_beos_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) { fprintf(stderr, "Drawing %i rectangles\n", num_rects); cairo_beos_surface_t *surface = reinterpret_cast( @@ -716,7 +822,7 @@ _cairo_beos_fill_rectangles (void *abstract_surface, surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); for (int i = 0; i < num_rects; ++i) { - _cairo_beos_fill_rectangle(surface, &rects[i]); + _cairo_beos_surface_fill_rectangle(surface, &rects[i]); } surface->view->PopState(); @@ -777,15 +883,16 @@ _cairo_beos_surface_get_extents (void *abstract_surface, } static const struct _cairo_surface_backend cairo_beos_surface_backend = { - NULL, /* create_similar */ + CAIRO_SURFACE_TYPE_BEOS, + _cairo_beos_surface_create_similar, _cairo_beos_surface_finish, _cairo_beos_surface_acquire_source_image, _cairo_beos_surface_release_source_image, _cairo_beos_surface_acquire_dest_image, _cairo_beos_surface_release_dest_image, NULL, /* clone_similar */ - _cairo_beos_composite, /* composite */ - _cairo_beos_fill_rectangles, + _cairo_beos_surface_composite, /* composite */ + _cairo_beos_surface_fill_rectangles, NULL, /* composite_trapezoids */ NULL, /* copy_page */ NULL, /* show_page */ @@ -806,6 +913,28 @@ static const struct _cairo_surface_backend cairo_beos_surface_backend = { NULL /* show_glyphs */ }; +static cairo_surface_t * +_cairo_beos_surface_create_internal (BView* view, + BBitmap* bmp, + bool owns_bitmap_view) +{ + // Must use malloc, because cairo code will use free() on the surface + cairo_beos_surface_t *surface = static_cast( + malloc(sizeof(cairo_beos_surface_t))); + if (surface == NULL) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return const_cast(&_cairo_surface_nil); + } + + _cairo_surface_init(&surface->base, &cairo_beos_surface_backend); + + surface->view = view; + surface->bitmap = bmp; + surface->owns_bitmap_view = owns_bitmap_view; + + return (cairo_surface_t *) surface; +} + /** * cairo_beos_surface_create: * @view: The view to draw on @@ -842,20 +971,7 @@ cairo_surface_t * cairo_beos_surface_create_for_bitmap (BView* view, BBitmap* bmp) { - // Must use malloc, because cairo code will use free() on the surface - cairo_beos_surface_t *surface = static_cast( - malloc(sizeof(cairo_beos_surface_t))); - if (surface == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return const_cast(&_cairo_surface_nil); - } - - _cairo_surface_init(&surface->base, &cairo_beos_surface_backend); - - surface->view = view; - surface->bitmap = bmp; - - return (cairo_surface_t *) surface; + return _cairo_beos_surface_create_internal(view, bmp); } // --------------------------------------------------------------------------- diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h index a75e8c0be..566dbe22d 100644 --- a/src/cairo-cache-private.h +++ b/src/cairo-cache-private.h @@ -88,7 +88,7 @@ typedef struct _cairo_cache_entry { } cairo_cache_entry_t; typedef cairo_bool_t -(*cairo_cache_keys_equal_func_t) (void *key_a, void *key_b); +(*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b); typedef void (*cairo_cache_callback_func_t) (void *entry, diff --git a/src/cairo-clip.c b/src/cairo-clip.c index c76ebdcbf..0c862a388 100644 --- a/src/cairo-clip.c +++ b/src/cairo-clip.c @@ -248,8 +248,10 @@ _cairo_clip_intersect_path (cairo_clip_t *clip, return CAIRO_STATUS_NO_MEMORY; status = _cairo_path_fixed_init_copy (&clip_path->path, path); - if (status) + if (status) { + free (clip_path); return status; + } clip_path->ref_count = 1; clip_path->fill_rule = fill_rule; diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c index be1791584..fcd5dd781 100644 --- a/src/cairo-directfb-surface.c +++ b/src/cairo-directfb-surface.c @@ -150,7 +150,7 @@ static inline int cairo_to_directfb_format(cairo_format_t format ) { return DSPF_A1; default: { - //assert(0); + /*assert(0);*/ return DSPF_UNKNOWN; } } @@ -483,9 +483,9 @@ _cairo_directfb_surface_composite (cairo_operator_t op, if( _dfb_set_operator(op,surface->buffer) == DFB_UNSUPPORTED ) return CAIRO_INT_STATUS_UNSUPPORTED; - if (src_pattern->type == CAIRO_PATTERN_SOLID ) { + if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID ) { - } else if (src_pattern->type != CAIRO_PATTERN_SURFACE || + } else if (src_pattern->type != CAIRO_PATTERN_TYPE_SURFACE || src_pattern->extend != CAIRO_EXTEND_NONE) { return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -494,7 +494,7 @@ _cairo_directfb_surface_composite (cairo_operator_t op, /* 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) + if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; @@ -662,6 +662,7 @@ _cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, static const cairo_surface_backend_t cairo_directfb_surface_backend = { + CAIRO_SURFACE_TYPE_DIRECTFB, _cairo_directfb_surface_create_similar, _cairo_directfb_surface_finish, _cairo_directfb_surface_acquire_source_image, diff --git a/src/cairo-font.c b/src/cairo-font.c index b0fab1b4a..425021a1e 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -130,6 +130,18 @@ cairo_font_face_destroy (cairo_font_face_t *font_face) free (font_face); } +/** + * cairo_font_face_get_type: + * @font_face: a #cairo_font_face_t + * + * Return value: The type of @font_face. See #cairo_font_type_t. + **/ +cairo_font_type_t +cairo_font_face_get_type (cairo_font_face_t *font_face) +{ + return font_face->backend->type; +} + /** * cairo_font_face_status: * @font_face: a #cairo_font_face_t @@ -198,8 +210,8 @@ cairo_font_face_set_user_data (cairo_font_face_t *font_face, static const cairo_font_face_backend_t _cairo_toy_font_face_backend; static int -_cairo_toy_font_face_keys_equal (void *key_a, - void *key_b); +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *key_b); /* We maintain a hash table from family/weight/slant => * cairo_font_face_t for cairo_toy_font_t. The primary purpose of @@ -300,11 +312,11 @@ _cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) } static int -_cairo_toy_font_face_keys_equal (void *key_a, - void *key_b) +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *key_b) { - cairo_toy_font_face_t *face_a = key_a; - cairo_toy_font_face_t *face_b = key_b; + const cairo_toy_font_face_t *face_a = key_a; + const cairo_toy_font_face_t *face_b = key_b; return (strcmp (face_a->family, face_b->family) == 0 && face_a->slant == face_b->slant && @@ -409,6 +421,7 @@ _cairo_toy_font_face_scaled_font_create (void *abstract_font_face } static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { + CAIRO_FONT_TYPE_TOY, _cairo_toy_font_face_destroy, _cairo_toy_font_face_scaled_font_create }; diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 45997444d..a23388a72 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -103,8 +103,8 @@ struct _cairo_ft_unscaled_font { }; static int -_cairo_ft_unscaled_font_keys_equal (void *key_a, - void *key_b); +_cairo_ft_unscaled_font_keys_equal (const void *key_a, + const void *key_b); static void _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); @@ -365,11 +365,11 @@ _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled) } static int -_cairo_ft_unscaled_font_keys_equal (void *key_a, - void *key_b) +_cairo_ft_unscaled_font_keys_equal (const void *key_a, + const void *key_b) { - cairo_ft_unscaled_font_t *unscaled_a = key_a; - cairo_ft_unscaled_font_t *unscaled_b = key_b; + const cairo_ft_unscaled_font_t *unscaled_a = key_a; + const cairo_ft_unscaled_font_t *unscaled_b = key_b; return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0 && unscaled_a->id == unscaled_b->id); @@ -1901,6 +1901,7 @@ _cairo_ft_show_glyphs (void *abstract_font, } const cairo_scaled_font_backend_t cairo_ft_scaled_font_backend = { + CAIRO_FONT_TYPE_FT, _cairo_ft_scaled_font_create_toy, _cairo_ft_scaled_font_fini, _cairo_ft_scaled_glyph_init, @@ -2004,6 +2005,7 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face, } static const cairo_font_face_backend_t _cairo_ft_font_face_backend = { + CAIRO_FONT_TYPE_FT, _cairo_ft_font_face_destroy, _cairo_ft_font_face_scaled_font_create }; diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c index b62547236..c000917ac 100644 --- a/src/cairo-glitz-surface.c +++ b/src/cairo-glitz-surface.c @@ -551,8 +551,8 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, attr->acquired = FALSE; switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; char *data; @@ -587,7 +587,7 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM)) break; - if (pattern->type == CAIRO_PATTERN_RADIAL) + if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) n_base_params = 6; else n_base_params = 4; @@ -639,7 +639,7 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, glitz_buffer_destroy (buffer); - if (pattern->type == CAIRO_PATTERN_LINEAR) + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; @@ -776,8 +776,8 @@ _cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, * 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 (src->type == CAIRO_PATTERN_TYPE_SOLID && + mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_color_t combined; cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; @@ -1018,7 +1018,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (pattern->type == CAIRO_PATTERN_SURFACE) + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { _cairo_pattern_init_copy (&tmp_src_pattern.base, pattern); @@ -2121,6 +2121,7 @@ _cairo_glitz_surface_flush (void *abstract_surface) } static const cairo_surface_backend_t cairo_glitz_surface_backend = { + CAIRO_SURFACE_TYPE_GLITZ, _cairo_glitz_surface_create_similar, _cairo_glitz_surface_finish, _cairo_glitz_surface_acquire_source_image, diff --git a/src/cairo-hash-private.h b/src/cairo-hash-private.h index 6dc9c9073..617b8410d 100644 --- a/src/cairo-hash-private.h +++ b/src/cairo-hash-private.h @@ -85,7 +85,7 @@ typedef struct _cairo_hash_entry { } cairo_hash_entry_t; typedef cairo_bool_t -(*cairo_hash_keys_equal_func_t) (void *key_a, void *key_b); +(*cairo_hash_keys_equal_func_t) (const void *key_a, const void *key_b); typedef cairo_bool_t (*cairo_hash_predicate_func_t) (void *entry); diff --git a/src/cairo-hash.c b/src/cairo-hash.c index e44ab3025..bfaac57ff 100644 --- a/src/cairo-hash.c +++ b/src/cairo-hash.c @@ -124,6 +124,7 @@ struct _cairo_hash_table { cairo_hash_entry_t **entries; unsigned long live_entries; + unsigned long iterating; /* Iterating, no insert, no resize */ }; /** @@ -163,6 +164,7 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) } hash_table->live_entries = 0; + hash_table->iterating = 0; return hash_table; } @@ -179,6 +181,10 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) * and this function will halt. The rationale for this behavior is to * avoid memory leaks and to avoid needless complication of the API * with destroy notifiy callbacks. + * + * WARNING: The hash_table must have no running iterators in it when + * _cairo_hash_table_destroy is called. It is a fatal error otherwise, + * and this function will halt. **/ void _cairo_hash_table_destroy (cairo_hash_table_t *hash_table) @@ -188,6 +194,8 @@ _cairo_hash_table_destroy (cairo_hash_table_t *hash_table) /* The hash table must be empty. Otherwise, halt. */ assert (hash_table->live_entries == 0); + /* No iterators can be running. Otherwise, halt. */ + assert (hash_table->iterating == 0); free (hash_table->entries); hash_table->entries = NULL; @@ -440,6 +448,9 @@ _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, * WARNING: It is a fatal error if an entry exists in the hash table * with a matching key, (this function will halt). * + * WARNING: It is a fatal error to insert an element while + * an iterator is running + * * Instead of using insert to replace an entry, consider just editing * the entry obtained with _cairo_hash_table_lookup. Or if absolutely * necessary, use _cairo_hash_table_remove first. @@ -454,6 +465,9 @@ _cairo_hash_table_insert (cairo_hash_table_t *hash_table, cairo_status_t status; cairo_hash_entry_t **entry; + /* Insert is illegal while an iterator is running. */ + assert (hash_table->iterating == 0); + entry = _cairo_hash_table_lookup_internal (hash_table, key_and_value, FALSE); @@ -498,11 +512,16 @@ _cairo_hash_table_remove (cairo_hash_table_t *hash_table, *entry = DEAD_ENTRY; hash_table->live_entries--; - /* This call _can_ fail, but only in failing to allocate new - * memory to shrink the hash table. It does leave the table in a - * consistent state, and we've already succeeded in removing the - * entry, so we don't examine the failure status of this call. */ - _cairo_hash_table_resize (hash_table); + /* Check for table resize. Don't do this when iterating as this will + * reorder elements of the table and cause the iteration to potentially + * skip some elements. */ + if (hash_table->iterating == 0) { + /* This call _can_ fail, but only in failing to allocate new + * memory to shrink the hash table. It does leave the table in a + * consistent state, and we've already succeeded in removing the + * entry, so we don't examine the failure status of this call. */ + _cairo_hash_table_resize (hash_table); + } } /** @@ -513,6 +532,12 @@ _cairo_hash_table_remove (cairo_hash_table_t *hash_table, * * Call @hash_callback for each live entry in the hash table, in a * non-specified order. + * + * Entries in @hash_table may be removed by code executed from @hash_callback. + * + * Entries may not be inserted to @hash_table, nor may @hash_table + * be destroyed by code executed from @hash_callback. The relevant + * functions will halt in these cases. **/ void _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, @@ -525,9 +550,17 @@ _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, if (hash_table == NULL) return; + /* Mark the table for iteration */ + ++hash_table->iterating; for (i = 0; i < hash_table->arrangement->size; i++) { entry = hash_table->entries[i]; if (ENTRY_IS_LIVE(entry)) hash_callback (entry, closure); } + /* If some elements were deleted during the iteration, + * the table may need resizing. Just do this every time + * as the check is inexpensive. + */ + if (--hash_table->iterating == 0) + _cairo_hash_table_resize (hash_table); } diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 108e89b5e..cef455b64 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -188,9 +188,10 @@ _create_pixman_format (cairo_format_t format) * @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 explicitly initialize the surface contents, using, for - * example, cairo_paint(). + * dimensions. Initially the surface contents are all + * 0. (Specifically, within each pixel, each color or alpha channel + * belonging to format will be 0. The contents of bits within a pixel, + * but not belonging to the given format are undefined). * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy when done @@ -861,7 +862,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, return status; } -static cairo_int_status_t +cairo_int_status_t _cairo_image_surface_set_clip_region (void *abstract_surface, pixman_region16_t *region) { @@ -903,6 +904,7 @@ _cairo_surface_is_image (const cairo_surface_t *surface) } const cairo_surface_backend_t cairo_image_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, _cairo_image_surface_create_similar, _cairo_image_surface_finish, _cairo_image_surface_acquire_source_image, diff --git a/src/cairo-lzw.c b/src/cairo-lzw.c new file mode 100644 index 000000000..2a4a8e1e7 --- /dev/null +++ b/src/cairo-lzw.c @@ -0,0 +1,400 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006 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 + */ + +#include "cairoint.h" + +typedef struct _lzw_buf { + cairo_status_t status; + + unsigned char *data; + int data_size; + int num_data; + uint32_t pending; + int pending_bits; +} lzw_buf_t; + +/* An lzw_buf_t is a simple, growable chunk of memory for holding + * variable-size objects of up to 16 bits each. + * + * Initialize an lzw_buf_t to the given size in bytes. + * + * To store objects into the lzw_buf_t, call _lzw_buf_store_bits and + * when finished, call _lzw_buf_store_pending, (which flushes out the + * last few bits that hadn't yet made a complete byte yet). + * + * Instead of returning failure from any functions, lzw_buf_t provides + * a status value that the caller can query, (and should query at + * least once when done with the object). The status value will be + * either CAIRO_STATUS_SUCCESS or CAIRO_STATUS_NO_MEMORY; + */ +static void +_lzw_buf_init (lzw_buf_t *buf, int size) +{ + if (size == 0) + size = 16; + + buf->status = CAIRO_STATUS_SUCCESS; + + buf->data = malloc (size); + if (buf->data == NULL) { + buf->data_size = 0; + buf->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + buf->data_size = size; + buf->num_data = 0; + buf->pending = 0; + buf->pending_bits = 0; +} + +/* Increase the buffer size by doubling. + * + * Returns CAIRO_STATUS_SUCCESS or CAIRO_STATUS_NO_MEMORY + */ +static cairo_status_t +_lzw_buf_grow (lzw_buf_t *buf) +{ + int new_size = buf->data_size * 2; + unsigned char *new_data; + + if (buf->status) + return buf->status; + + new_data = realloc (buf->data, new_size); + if (new_data == NULL) { + free (buf->data); + buf->data_size = 0; + buf->status = CAIRO_STATUS_NO_MEMORY; + return buf->status; + } + + buf->data = new_data; + buf->data_size = new_size; + + return CAIRO_STATUS_SUCCESS; +} + +/* Store the lowest num_bits bits of values into buf. + * + * NOTE: The bits of value above size_in_bits must be 0, (so don't lie + * about the size). + * + * See also _lzw_buf_store_pending which must be called after the last + * call to _lzw_buf_store_bits. + * + * Sets buf->status to either CAIRO_STATUS_SUCCESS or CAIRO_STATUS_NO_MEMORY. + */ +static void +_lzw_buf_store_bits (lzw_buf_t *buf, uint16_t value, int num_bits) +{ + cairo_status_t status; + + assert (value <= (1 << num_bits) - 1); + + if (buf->status) + return; + + buf->pending = (buf->pending << num_bits) | value; + buf->pending_bits += num_bits; + + while (buf->pending_bits >= 8) { + if (buf->num_data >= buf->data_size) { + status = _lzw_buf_grow (buf); + if (status) + return; + } + buf->data[buf->num_data++] = buf->pending >> (buf->pending_bits - 8); + buf->pending_bits -= 8; + } +} + +/* Store the last remaining pending bits into the buffer. + * + * NOTE: This function must be called after the last call to + * _lzw_buf_store_bits. + * + * Sets buf->status to either CAIRO_STATUS_SUCCESS or CAIRO_STATUS_NO_MEMORY. + */ +static void +_lzw_buf_store_pending (lzw_buf_t *buf) +{ + cairo_status_t status; + + if (buf->status) + return; + + if (buf->pending_bits == 0) + return; + + assert (buf->pending_bits < 8); + + if (buf->num_data >= buf->data_size) { + status = _lzw_buf_grow (buf); + if (status) + return; + } + + buf->data[buf->num_data++] = buf->pending << (8 - buf->pending_bits); + buf->pending_bits = 0; +} + +/* LZW defines a few magic code values */ +#define LZW_CODE_CLEAR_TABLE 256 +#define LZW_CODE_EOD 257 +#define LZW_CODE_FIRST 258 + +/* We pack three separate values into a symbol as follows: + * + * 12 bits (31 down to 20): CODE: code value used to represent this symbol + * 12 bits (19 down to 8): PREV: previous code value in chain + * 8 bits ( 7 down to 0): NEXT: next byte value in chain + */ +typedef uint32_t lzw_symbol_t; + +#define LZW_SYMBOL_SET(sym, prev, next) ((sym) = ((prev) << 8)|(next)) +#define LZW_SYMBOL_SET_CODE(sym, code, prev, next) ((sym) = ((code << 20)|(prev) << 8)|(next)) +#define LZW_SYMBOL_GET_CODE(sym) (((sym) >> 20)) +#define LZW_SYMBOL_GET_PREV(sym) (((sym) >> 8) & 0x7ff) +#define LZW_SYMBOL_GET_BYTE(sym) (((sym) >> 0) & 0x0ff) + +/* The PREV+NEXT fields can be seen as the key used to fetch values + * from the hash table, while the code is the value fetched. + */ +#define LZW_SYMBOL_KEY_MASK 0x000fffff + +/* Since code values are only stored starting with 258 we can safely + * use a zero value to represent free slots in the hash table. */ +#define LZW_SYMBOL_FREE 0x00000000 + +/* These really aren't very free for modifying. First, the PostScript + * specification sets the 9-12 bit range. Second, the encoding of + * lzw_symbol_t above also relies on 2 of LZW_BITS_MAX plus one byte + * fitting within 32 bits. + * + * But other than that, the LZW compression scheme could function with + * more bits per code. + */ +#define LZW_BITS_MIN 9 +#define LZW_BITS_MAX 12 +#define LZW_BITS_BOUNDARY(bits) ((1<<(bits))-1) +#define LZW_MAX_SYMBOLS (1<table, 0, LZW_SYMBOL_TABLE_SIZE * sizeof (lzw_symbol_t)); +} + +/* Lookup a symbol in the symbol table. The PREV and NEXT fields of + * symbol form the key for the lookup. + * + * If succesful, then this function returns TRUE and slot_ret will be + * left pointing at the result that will have the CODE field of + * interest. + * + * If the lookup fails, then this function returns FALSE and slot_ret + * will be pointing at the location in the table to which a new CODE + * value should be stored along with PREV and NEXT. + */ +static cairo_bool_t +_lzw_symbol_table_lookup (lzw_symbol_table_t *table, + lzw_symbol_t symbol, + lzw_symbol_t **slot_ret) +{ + /* The algorithm here is identical to that in cairo-hash.c. We + * copy it here to allow for a rather more efficient + * implementation due to several circumstances that do not apply + * to the more general case: + * + * 1) We have a known bound on the total number of symbols, so we + * have a fixed-size table without any copying when growing + * + * 2) We never delete any entries, so we don't need to + * support/check for DEAD entries during lookup. + * + * 3) The object fits in 32 bits so we store each object in its + * entirety within the table rather than storing objects + * externally and putting pointers in the table, (which here + * would just double the storage requirements and have negative + * impacts on memory locality). + */ + int i, idx, step, hash = symbol & LZW_SYMBOL_KEY_MASK; + lzw_symbol_t candidate; + + idx = hash % LZW_SYMBOL_MOD1; + step = 0; + + *slot_ret = NULL; + for (i = 0; i < LZW_SYMBOL_TABLE_SIZE; i++) + { + candidate = table->table[idx]; + if (candidate == LZW_SYMBOL_FREE) + { + *slot_ret = &table->table[idx]; + return FALSE; + } + else /* candidate is LIVE */ + { + if ((candidate & LZW_SYMBOL_KEY_MASK) == + (symbol & LZW_SYMBOL_KEY_MASK)) + { + *slot_ret = &table->table[idx]; + return TRUE; + } + } + + if (step == 0) { + step = hash % LZW_SYMBOL_MOD2; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= LZW_SYMBOL_TABLE_SIZE) + idx -= LZW_SYMBOL_TABLE_SIZE; + } + + return FALSE; +} + +/* Compress a bytestream using the LZW algorithm. + * + * This is an original implementation based on reading the + * specification of the LZWDecode filter in the PostScript Language + * Reference. The free parameters in the LZW algorithm are set to the + * values mandated by PostScript, (symbols encoded with widths from 9 + * to 12 bits). + * + * This function returns a pointer to a newly allocated buffer holding + * the compressed data, or NULL if an out-of-memory situation + * occurs. + * + * Notice that any one of the _lzw_buf functions called here could + * trigger an out-of-memory condition. But lzw_buf_t uses cairo's + * shutdown-on-error idiom, so it's safe to continue to call into + * lzw_buf without having to check for errors, (until a final check at + * the end). + */ +cairo_public unsigned char * +_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out) +{ + int bytes_remaining = *size_in_out; + lzw_buf_t buf; + lzw_symbol_table_t table; + lzw_symbol_t symbol, *slot; + int code_next = LZW_CODE_FIRST; + int code_bits = LZW_BITS_MIN; + int prev, next; + + if (*size_in_out == 0) + return NULL; + + _lzw_buf_init (&buf, *size_in_out); + + _lzw_symbol_table_init (&table); + + /* The LZW header is a clear table code. */ + _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits); + + while (1) { + + /* Find the longest existing code in the symbol table that + * matches the current input, if any. */ + prev = *data++; + bytes_remaining--; + if (bytes_remaining) { + do + { + next = *data++; + bytes_remaining--; + LZW_SYMBOL_SET (symbol, prev, next); + if (_lzw_symbol_table_lookup (&table, symbol, &slot)) + prev = LZW_SYMBOL_GET_CODE (*slot); + } while (bytes_remaining && *slot != LZW_SYMBOL_FREE); + if (*slot == LZW_SYMBOL_FREE) { + data--; + bytes_remaining++; + } + } + + /* Write the code into the output. This is either a byte read + * directly from the input, or a code from the last successful + * lookup. */ + _lzw_buf_store_bits (&buf, prev, code_bits); + + if (bytes_remaining == 0) + break; + + LZW_SYMBOL_SET_CODE (*slot, code_next++, prev, next); + + if (code_next > LZW_BITS_BOUNDARY(code_bits)) + { + code_bits++; + if (code_bits > LZW_BITS_MAX) { + _lzw_symbol_table_init (&table); + _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits - 1); + code_bits = LZW_BITS_MIN; + code_next = LZW_CODE_FIRST; + } + } + } + + /* The LZW footer is an end-of-data code. */ + _lzw_buf_store_bits (&buf, LZW_CODE_EOD, code_bits); + + _lzw_buf_store_pending (&buf); + + /* See if we ever ran out of memory while writing to buf. */ + if (buf.status == CAIRO_STATUS_NO_MEMORY) { + *size_in_out = 0; + return NULL; + } + + assert (buf.status == CAIRO_STATUS_SUCCESS); + + *size_in_out = buf.num_data; + return buf.data; +} diff --git a/src/cairo-meta-surface-private.h b/src/cairo-meta-surface-private.h index e57150904..eafe3f441 100644 --- a/src/cairo-meta-surface-private.h +++ b/src/cairo-meta-surface-private.h @@ -53,7 +53,7 @@ typedef enum { * fallbacks should never get triggered). So the plan is to * eliminate as many of these as possible. */ - CAIRO_COMMAND_INTERSECT_CLIP_PATH, + CAIRO_COMMAND_INTERSECT_CLIP_PATH } cairo_command_type_t; diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c index a314150bc..f7aeb719c 100644 --- a/src/cairo-meta-surface.c +++ b/src/cairo-meta-surface.c @@ -201,7 +201,7 @@ _init_pattern_with_snapshot (cairo_pattern_t *pattern, { _cairo_pattern_init_copy (pattern, other); - if (pattern->type == CAIRO_PATTERN_SURFACE) { + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; @@ -557,6 +557,7 @@ _cairo_surface_is_meta (const cairo_surface_t *surface) } static const cairo_surface_backend_t cairo_meta_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_META, _cairo_meta_surface_create_similar, _cairo_meta_surface_finish, _cairo_meta_surface_acquire_source_image, diff --git a/src/cairo-operator.c b/src/cairo-operator.c new file mode 100644 index 000000000..99c3aaaa0 --- /dev/null +++ b/src/cairo-operator.c @@ -0,0 +1,119 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006 Keith Packard + * Copyright © 2006 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 + * Keith Packard + */ + +#include "cairoint.h" + +/* The analysis here assumes destination alpha semantics (that is + * CAIRO_CONTENT_COLOR_ALPHA). More things can be considered opaque + * otherwise (CAIRO_CONTENT_COLOR) so we'll probably want to add a + * cairo_content_t parameter to this function + * + * We also need a definition of what "opaque" means. Is it, "does not + * requiring 'knowing' the original contents of destination, nor does + * it set the destination alpha to anything but 1.0" ? + */ +cairo_bool_t +_cairo_operator_always_opaque (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return FALSE; + + case CAIRO_OPERATOR_SOURCE: + return FALSE; + + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + return FALSE; + + case CAIRO_OPERATOR_DEST: + return TRUE; + + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return FALSE; + } + return FALSE; +} + +/* As above, we'll probably want to add a cairo_content_t parameter to + * this function + * + * We also need a definition of what "translucent" means. + */ +cairo_bool_t +_cairo_operator_always_translucent (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return TRUE; + + case CAIRO_OPERATOR_SOURCE: + return FALSE; + + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + return FALSE; + + case CAIRO_OPERATOR_DEST: + return FALSE; + + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + return TRUE; + } + return TRUE; +} diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index a6db09198..e9b572786 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -44,54 +44,92 @@ #endif /* _MSC_VER */ struct _cairo_output_stream { - cairo_write_func_t write_data; + cairo_write_func_t write_func; + cairo_close_func_t close_func; void *closure; - cairo_bool_t owns_closure_is_file; unsigned long position; cairo_status_t status; + cairo_bool_t closed; +}; + +const cairo_output_stream_t cairo_output_stream_nil = { + NULL, /* write_func */ + NULL, /* close_func */ + NULL, /* closure */ + 0, /* position */ + CAIRO_STATUS_NO_MEMORY, + FALSE /* closed */ +}; + +static const cairo_output_stream_t cairo_output_stream_nil_write_error = { + NULL, /* write_func */ + NULL, /* close_func */ + NULL, /* closure */ + 0, /* position */ + CAIRO_STATUS_WRITE_ERROR, + FALSE /* closed */ }; cairo_output_stream_t * -_cairo_output_stream_create (cairo_write_func_t write_data, +_cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, void *closure) { cairo_output_stream_t *stream; stream = malloc (sizeof (cairo_output_stream_t)); if (stream == NULL) - return NULL; + return (cairo_output_stream_t *) &cairo_output_stream_nil; - stream->write_data = write_data; + stream->write_func = write_func; + stream->close_func = close_func; stream->closure = closure; - stream->owns_closure_is_file = FALSE; stream->position = 0; stream->status = CAIRO_STATUS_SUCCESS; + stream->closed = FALSE; return stream; } +void +_cairo_output_stream_close (cairo_output_stream_t *stream) +{ + cairo_status_t status; + + if (stream->closed) + return; + + if (stream->close_func) { + status = stream->close_func (stream->closure); + if (status) + stream->status = status; + } + + stream->closed = TRUE; +} + void _cairo_output_stream_destroy (cairo_output_stream_t *stream) { - if (stream->owns_closure_is_file) { - FILE *file = stream->closure; - fflush (file); - fclose (file); - } + if (stream == NULL) + return; + + _cairo_output_stream_close (stream); free (stream); } -cairo_status_t +void _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length) { if (length == 0) - return CAIRO_STATUS_SUCCESS; + return; - stream->status = stream->write_data (stream->closure, data, length); + if (stream->status) + return; + + stream->status = stream->write_func (stream->closure, data, length); stream->position += length; - - return stream->status; } void @@ -103,6 +141,9 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, char buffer[2]; int i, column; + if (stream->status) + return; + for (i = 0, column = 0; i < length; i++, column++) { if (column == 38) { _cairo_output_stream_write (stream, "\n", 1); @@ -175,8 +216,7 @@ enum { * formatting. This functionality is only for internal use and we * only implement the formats we actually use. */ - -cairo_status_t +void _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *fmt, va_list ap) { @@ -185,6 +225,9 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *f; int length_modifier; + if (stream->status) + return; + f = fmt; p = buffer; while (*f != '\0') { @@ -247,24 +290,19 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, } _cairo_output_stream_write (stream, buffer, p - buffer); - - return stream->status; } -cairo_status_t +void _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); + _cairo_output_stream_vprintf (stream, fmt, ap); va_end (ap); - - return status; } long @@ -286,28 +324,57 @@ _cairo_output_stream_get_status (cairo_output_stream_t *stream) static cairo_status_t stdio_write (void *closure, const unsigned char *data, unsigned int length) { - FILE *fp = closure; + FILE *file = closure; - if (fwrite (data, 1, length, fp) == length) + if (fwrite (data, 1, length, file) != length) + return CAIRO_STATUS_WRITE_ERROR; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +stdio_flush (void *closure) +{ + FILE *file = closure; + + fflush (file); + + if (ferror (file)) + return CAIRO_STATUS_WRITE_ERROR; + else return CAIRO_STATUS_SUCCESS; +} - return CAIRO_STATUS_WRITE_ERROR; +static cairo_status_t +stdio_close (void *closure) +{ + cairo_status_t status; + FILE *file = closure; + + status = stdio_flush (closure); + + fclose (file); + + return status; } cairo_output_stream_t * -_cairo_output_stream_create_for_file (const char *filename) +_cairo_output_stream_create_for_file (FILE *file) { - FILE *fp; - cairo_output_stream_t *stream; + if (file == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil_write_error; - 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; + return _cairo_output_stream_create (stdio_write, stdio_flush, file); +} + +cairo_output_stream_t * +_cairo_output_stream_create_for_filename (const char *filename) +{ + FILE *file; + + file = fopen (filename, "wb"); + if (file == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil_write_error; + + return _cairo_output_stream_create (stdio_write, stdio_close, file); } diff --git a/src/cairo-paginated-surface-private.h b/src/cairo-paginated-surface-private.h index 79438e4ab..b5e4d5c9b 100644 --- a/src/cairo-paginated-surface-private.h +++ b/src/cairo-paginated-surface-private.h @@ -38,11 +38,99 @@ #include "cairoint.h" +typedef enum { + CAIRO_PAGINATED_MODE_ANALYZE, /* analyze page regions */ + CAIRO_PAGINATED_MODE_RENDER /* render page contents */ +} cairo_paginated_mode_t; + +typedef struct _cairo_paginated_surface_backend { + /* Optional. Will be called once for each page. + * + * NOTE: With respect to the order of drawing operations as seen + * by the target, this call will occur before any drawing + * operations for the relevant page. However, with respect to the + * function calls as made by the user, this call will be *after* + * any drawing operations for the page, (that is, it will occur + * during the user's call to cairo_show_page or cairo_copy_page). + */ + cairo_int_status_t + (*start_page) (void *surface); + + /* Required. Will be called twice for each page, once with an + * argument of CAIRO_PAGINATED_MODE_ANALYZE and once with + * CAIRO_PAGINATED_MODE_RENDER. See more details in the + * documentation for _cairo_paginated_surface_create below. + */ + void + (*set_paginated_mode) (void *surface, + cairo_paginated_mode_t mode); +} cairo_paginated_surface_backend_t; + +/* A cairo_paginated_surface provides a very convenient wrapper that + * is well-suited for doing the analysis common to most surfaces that + * have paginated output, (that is, things directed at printers, or + * for saving content in files such as PostScript or PDF files). + * + * To use the paginated surface, you'll first need to create your + * 'real' surface using _cairo_surface_init and the standard + * cairo_surface_backend_t. Then you also call + * _cairo_paginated_surface_create which takes its own, much simpler, + * cairo_paginated_surface_backend. You are free to return the result + * of _cairo_paginated_surface_create from your public + * cairo__surface_create. The paginated backend will be careful + * to not let the user see that they really got a "wrapped" + * surface. See test-paginated-surface.c for a fairly minimal example + * of a paginated-using surface. That should be a reasonable example + * to follow. + * + * What the paginated surface does is first save all drawing + * operations for a page into a meta-surface. Then when the user calls + * cairo_show_page, the paginated surface performs the following + * sequence of operations (using the backend functions passed to + * cairo_paginated_surface_create): + * + * 1. Calls start_page (if non NULL). At this point, it is appropriate + * for the target to emit any page-specific header information into + * its output. + * + * 2. Calls set_paginated_mode with an argument of CAIRO_PAGINATED_MODE_ANALYZE + * + * 3. Replays the meta-surface to the target surface, (with an + * analysis surface inserted between which watches the return value + * from each operation). This analysis stage is used to decide which + * operations will require fallbacks. + * + * 4. Calls set_paginated_mode with an argument of CAIRO_PAGINATED_MODE_RENDER + * + * 5. Replays a subset of the meta-surface operations to the target surface + * + * 6. Replays the remaining operations to an image surface, sets an + * appropriate clip on the target, then paints the resulting image + * surface to the target. + * + * So, the target will see drawing operations during two separate + * stages, (ANALYZE and RENDER). During the ANALYZE phase the target + * should not actually perform any rendering, (for example, if + * performing output to a file, no output should be generated during + * this stage). Instead the drawing functions simply need to return + * CAIRO_STATUS_SUCCESS or CAIRO_INT_STATUS_UNSUPPORTED to indicate + * whether rendering would be supported. And it should do this as + * quickly as possible. + * + * NOTE: The paginated surface layer assumes that the target surface + * is "blank" by default at the beginning of each page, without any + * need for an explicit erasea operation, (as opposed to an image + * surface, for example, which might have uninitialized content + * originally). As such, it optimizes away CLEAR operations that + * happen at the beginning of each page---the target surface will not + * even see these operations. + */ cairo_private cairo_surface_t * -_cairo_paginated_surface_create (cairo_surface_t *target, - cairo_content_t content, - int width, - int height); +_cairo_paginated_surface_create (cairo_surface_t *target, + cairo_content_t content, + int width, + int height, + const cairo_paginated_surface_backend_t *backend); cairo_private cairo_surface_t * _cairo_paginated_surface_get_target (cairo_surface_t *surface); diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index 6317d7083..34b42e777 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -31,47 +31,27 @@ * * Contributor(s): * Carl Worth + * Keith Packard */ /* The paginated surface layer exists to provide as much code sharing * as possible for the various paginated surface backends in cairo - * (PostScript, PDF, etc.). - * - * The concept is that a surface which uses a paginated surface merely - * needs to implement backend operations which it can accurately - * provide, (and return CAIRO_INT_STATUS_UNSUPPORTED or leave backend - * function pointers NULL otherwise). The paginated surface is the - * responsible for collecting operations that aren't supported, - * replaying them against the image surface, and then supplying the - * resulting images to the target surface. - * - * When created, a paginated surface accepts the target surface to - * which the final drawing will eventually be performed. The paginated - * surface then uses cairo_meta_surface_t to record all drawing - * operations up until each show_page operation. - * - * At the time of show_page, the paginated surface replays the meta - * surface against the target surface and maintains regions of the - * result that will come from the nativ surface and regions that will - * need to come from image fallbacks. It then replays the necessary - * portions against image surface and provides those results to the - * target surface through existing interfaces. - * - * This way the target surface is never even aware of any distinction - * between native drawing operations vs. results that are supplied by - * image fallbacks. Instead the surface need only implement as much of - * the surface backend interface as it can do correctly, and let the - * paginated surface take care of all the messy details. + * (PostScript, PDF, etc.). See cairo-paginated-surface-private.h for + * more details on how it works and how to use it. */ #include "cairoint.h" #include "cairo-paginated-surface-private.h" #include "cairo-meta-surface-private.h" +#include "cairo-analysis-surface-private.h" typedef struct _cairo_paginated_surface { cairo_surface_t base; + /* The target surface to hold the final result. */ + cairo_surface_t *target; + cairo_content_t content; /* XXX: These shouldn't actually exist. We inherit this ugliness @@ -82,14 +62,16 @@ typedef struct _cairo_paginated_surface { int width; int height; - /* The target surface to hold the final result. */ - cairo_surface_t *target; + /* Paginated-surface specific functions for the target */ + const cairo_paginated_surface_backend_t *backend; /* A cairo_meta_surface to record all operations. To be replayed * against target, and also against image surface as necessary for * fallbacks. */ cairo_surface_t *meta; + cairo_bool_t page_is_blank; + } cairo_paginated_surface_t; const cairo_private cairo_surface_backend_t cairo_paginated_surface_backend; @@ -97,11 +79,30 @@ const cairo_private cairo_surface_backend_t cairo_paginated_surface_backend; static cairo_int_status_t _cairo_paginated_surface_show_page (void *abstract_surface); +/* XXX: This would seem the natural thing to do here. But currently, + * PDF and PS surfaces do not yet work as source surfaces. So instead, + * we don't implement create_similar for the paginate_surface which + * means that any create_similar() call on a paginated_surfacae will + * result in a new image surface. */ +#if 0 +static cairo_surface_t * +_cairo_paginated_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_paginated_surface_t *surface = abstract_surface; + return cairo_surface_create_similar (surface->target, content, + width, height); +} +#endif + cairo_surface_t * -_cairo_paginated_surface_create (cairo_surface_t *target, - cairo_content_t content, - int width, - int height) +_cairo_paginated_surface_create (cairo_surface_t *target, + cairo_content_t content, + int width, + int height, + const cairo_paginated_surface_backend_t *backend) { cairo_paginated_surface_t *surface; @@ -111,16 +112,24 @@ _cairo_paginated_surface_create (cairo_surface_t *target, _cairo_surface_init (&surface->base, &cairo_paginated_surface_backend); + /* Override surface->base.type with target's type so we don't leak + * evidence of the paginated wrapper out to the user. */ + surface->base.type = cairo_surface_get_type (target); + + surface->target = target; + surface->content = content; surface->width = width; surface->height = height; - surface->target = target; + surface->backend = backend; surface->meta = _cairo_meta_surface_create (content, width, height); if (cairo_surface_status (surface->meta)) goto FAIL_CLEANUP_SURFACE; + surface->page_is_blank = TRUE; + return &surface->base; FAIL_CLEANUP_SURFACE: @@ -191,32 +200,70 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } -static void +static cairo_int_status_t _paint_page (cairo_paginated_surface_t *surface) { + cairo_surface_t *analysis; cairo_surface_t *image; cairo_pattern_t *pattern; + cairo_status_t status; - image = _cairo_image_surface_create_with_content (surface->content, - surface->width, - surface->height); + analysis = _cairo_analysis_surface_create (surface->target, + surface->width, surface->height); - _cairo_meta_surface_replay (surface->meta, image); + surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE); + _cairo_meta_surface_replay (surface->meta, analysis); + surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER); - pattern = cairo_pattern_create_for_surface (image); + if (analysis->status) { + status = analysis->status; + cairo_surface_destroy (analysis); + return status; + } + + if (_cairo_analysis_surface_has_unsupported (analysis)) + { + image = _cairo_image_surface_create_with_content (surface->content, + surface->width, + surface->height); - _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, pattern); + _cairo_meta_surface_replay (surface->meta, image); - cairo_pattern_destroy (pattern); + pattern = cairo_pattern_create_for_surface (image); - cairo_surface_destroy (image); + _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, pattern); + + cairo_pattern_destroy (pattern); + + cairo_surface_destroy (image); + } + else + { + _cairo_meta_surface_replay (surface->meta, surface->target); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_start_page (cairo_paginated_surface_t *surface) +{ + if (! surface->backend->start_page) + return CAIRO_STATUS_SUCCESS; + + return (surface->backend->start_page) (surface->target); } static cairo_int_status_t _cairo_paginated_surface_copy_page (void *abstract_surface) { + cairo_status_t status; cairo_paginated_surface_t *surface = abstract_surface; + status = _start_page (surface); + if (status) + return status; + _paint_page (surface); /* XXX: It might make sense to add some suport here for calling @@ -235,8 +282,13 @@ _cairo_paginated_surface_copy_page (void *abstract_surface) static cairo_int_status_t _cairo_paginated_surface_show_page (void *abstract_surface) { + cairo_status_t status; cairo_paginated_surface_t *surface = abstract_surface; + status = _start_page (surface); + if (status) + return status; + _paint_page (surface); _cairo_surface_show_page (surface->target); @@ -248,6 +300,8 @@ _cairo_paginated_surface_show_page (void *abstract_surface) if (cairo_surface_status (surface->meta)) return cairo_surface_status (surface->meta); + surface->page_is_blank = TRUE; + return CAIRO_STATUS_SUCCESS; } @@ -281,6 +335,12 @@ _cairo_paginated_surface_paint (void *abstract_surface, { cairo_paginated_surface_t *surface = abstract_surface; + /* Optimize away erasing of nothing. */ + if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + surface->page_is_blank = FALSE; + return _cairo_surface_paint (surface->meta, op, source); } @@ -308,6 +368,12 @@ _cairo_paginated_surface_stroke (void *abstract_surface, { cairo_paginated_surface_t *surface = abstract_surface; + /* Optimize away erasing of nothing. */ + if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + surface->page_is_blank = FALSE; + return _cairo_surface_stroke (surface->meta, op, source, path, style, ctm, ctm_inverse, @@ -325,6 +391,12 @@ _cairo_paginated_surface_fill (void *abstract_surface, { cairo_paginated_surface_t *surface = abstract_surface; + /* Optimize away erasing of nothing. */ + if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + surface->page_is_blank = FALSE; + return _cairo_surface_fill (surface->meta, op, source, path, fill_rule, tolerance, antialias); @@ -340,6 +412,12 @@ _cairo_paginated_surface_show_glyphs (void *abstract_surface, { cairo_paginated_surface_t *surface = abstract_surface; + /* Optimize away erasing of nothing. */ + if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + surface->page_is_blank = FALSE; + return _cairo_surface_show_glyphs (surface->meta, op, source, glyphs, num_glyphs, scaled_font); @@ -381,7 +459,8 @@ _cairo_paginated_surface_snapshot (void *abstract_other) } const cairo_surface_backend_t cairo_paginated_surface_backend = { - NULL, /* create_similar */ + CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, + NULL, /* create_similar --- see note for _cairo_paginated_surface_create_similar */ _cairo_paginated_surface_finish, _cairo_paginated_surface_acquire_source_image, _cairo_paginated_surface_release_source_image, diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index e8170990e..7ca6ab811 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -115,7 +115,11 @@ _cairo_stroker_start_dash (cairo_stroker_t *stroker) int i = 0; offset = stroker->style->dash_offset; - while (offset >= stroker->style->dash[i]) { + + /* We stop searching for a starting point as soon as the + offset reaches zero. Otherwise when an initial dash + segment shrinks to zero it will be skipped over. */ + while (offset > 0.0 && offset >= stroker->style->dash[i]) { offset -= stroker->style->dash[i]; on = 1-on; if (++i == stroker->style->num_dashes) @@ -546,11 +550,18 @@ _compute_face (cairo_point_t *point, cairo_slope_t *slope, cairo_stroker_t *stro 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_slope_t *slope, cairo_stroke_face_t *start, + cairo_stroke_face_t *end) { cairo_status_t status; cairo_polygon_t polygon; - cairo_slope_t slope; + + _compute_face (p1, slope, stroker, 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, stroker, end); if (p1->x == p2->x && p1->y == p2->y) { /* XXX: Need to rethink how this case should be handled, (both @@ -559,14 +570,6 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_ return CAIRO_STATUS_SUCCESS; } - _cairo_slope_init (&slope, p1, p2); - _compute_face (p1, &slope, stroker, 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, stroker, 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 @@ -612,6 +615,16 @@ _cairo_stroker_move_to (void *closure, cairo_point_t *point) return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_stroker_move_to_dashed (void *closure, cairo_point_t *point) +{ + /* reset the dash pattern for new sub paths */ + cairo_stroker_t *stroker = closure; + _cairo_stroker_start_dash (stroker); + + return _cairo_stroker_move_to (closure, point); +} + static cairo_status_t _cairo_stroker_line_to (void *closure, cairo_point_t *point) { @@ -620,6 +633,7 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point) cairo_stroke_face_t start, end; cairo_point_t *p1 = &stroker->current_point; cairo_point_t *p2 = point; + cairo_slope_t slope; if (p1->x == p2->x && p1->y == p2->y) { /* XXX: Need to rethink how this case should be handled, (both @@ -628,8 +642,10 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point) as possible. */ return CAIRO_STATUS_SUCCESS; } + + _cairo_slope_init (&slope, p1, p2); - status = _cairo_stroker_add_sub_edge (stroker, p1, p2, &start, &end); + status = _cairo_stroker_add_sub_edge (stroker, p1, p2, &slope, &start, &end); if (status) return status; @@ -667,6 +683,17 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) cairo_stroke_face_t sub_start, sub_end; cairo_point_t *p1 = &stroker->current_point; cairo_point_t *p2 = point; + 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 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; + } + + _cairo_slope_init (&slope, p1, p2); dx = _cairo_fixed_to_double (p2->x - p1->x); dy = _cairo_fixed_to_double (p2->y - p1->y); @@ -692,7 +719,7 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) * XXX simplify this case analysis */ if (stroker->dash_on) { - status = _cairo_stroker_add_sub_edge (stroker, &fd1, &fd2, &sub_start, &sub_end); + status = _cairo_stroker_add_sub_edge (stroker, &fd1, &fd2, &slope, &sub_start, &sub_end); if (status) return status; if (!first) { @@ -942,7 +969,7 @@ _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, if (stroker.style->dash) status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_stroker_move_to, + _cairo_stroker_move_to_dashed, _cairo_stroker_line_to_dashed, _cairo_stroker_curve_to_dashed, _cairo_stroker_close_path, diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index d7b73693b..3f0b5e457 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -30,7 +30,7 @@ #include "cairoint.h" const cairo_solid_pattern_t cairo_pattern_nil = { - { CAIRO_PATTERN_SOLID, /* type */ + { CAIRO_PATTERN_TYPE_SOLID, /* type */ (unsigned int)-1, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ @@ -39,7 +39,7 @@ const cairo_solid_pattern_t cairo_pattern_nil = { }; static const cairo_solid_pattern_t cairo_pattern_nil_null_pointer = { - { CAIRO_PATTERN_SOLID, /* type */ + { CAIRO_PATTERN_TYPE_SOLID, /* type */ (unsigned int)-1, /* ref_count */ CAIRO_STATUS_NULL_POINTER,/* status */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ @@ -48,7 +48,7 @@ static const cairo_solid_pattern_t cairo_pattern_nil_null_pointer = { }; static const cairo_solid_pattern_t cairo_pattern_nil_file_not_found = { - { CAIRO_PATTERN_SOLID, /* type */ + { CAIRO_PATTERN_TYPE_SOLID, /* type */ (unsigned int)-1, /* ref_count */ CAIRO_STATUS_FILE_NOT_FOUND, /* status */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ @@ -57,7 +57,7 @@ static const cairo_solid_pattern_t cairo_pattern_nil_file_not_found = { }; static const cairo_solid_pattern_t cairo_pattern_nil_read_error = { - { CAIRO_PATTERN_SOLID, /* type */ + { CAIRO_PATTERN_TYPE_SOLID, /* type */ (unsigned int)-1, /* ref_count */ CAIRO_STATUS_READ_ERROR, /* status */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ @@ -117,7 +117,7 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) pattern->ref_count = 1; pattern->status = CAIRO_STATUS_SUCCESS; - if (type == CAIRO_PATTERN_SURFACE) + if (type == CAIRO_PATTERN_TYPE_SURFACE) pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT; else pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT; @@ -131,7 +131,7 @@ static void _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, const cairo_gradient_pattern_t *other) { - if (other->base.type == CAIRO_PATTERN_LINEAR) + if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; @@ -170,21 +170,21 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, } switch (other->type) { - case CAIRO_PATTERN_SOLID: { + case CAIRO_PATTERN_TYPE_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: { + case CAIRO_PATTERN_TYPE_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: { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; @@ -199,16 +199,16 @@ void _cairo_pattern_fini (cairo_pattern_t *pattern) { switch (pattern->type) { - case CAIRO_PATTERN_SOLID: + case CAIRO_PATTERN_TYPE_SOLID: break; - case CAIRO_PATTERN_SURFACE: { + case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; cairo_surface_destroy (surface_pattern->surface); } break; - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; @@ -222,7 +222,7 @@ void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, const cairo_color_t *color) { - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID); + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); pattern->color = *color; } @@ -232,12 +232,12 @@ _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, { if (surface->status) { /* Force to solid to simplify the pattern_fini process. */ - pattern->base.type = CAIRO_PATTERN_SOLID; + pattern->base.type = CAIRO_PATTERN_TYPE_SOLID; _cairo_pattern_set_error (&pattern->base, surface->status); return; } - _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE); + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE); pattern->surface = cairo_surface_reference (surface); } @@ -256,7 +256,7 @@ 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); + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR); pattern->gradient.p1.x = _cairo_fixed_from_double (x0); pattern->gradient.p1.y = _cairo_fixed_from_double (y0); @@ -269,7 +269,7 @@ _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); + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL); pattern->gradient.inner.x = _cairo_fixed_from_double (cx0); pattern->gradient.inner.y = _cairo_fixed_from_double (cy0); @@ -524,6 +524,18 @@ cairo_pattern_reference (cairo_pattern_t *pattern) return pattern; } +/** + * cairo_pattern_get_type: + * @pattern: a #cairo_pattern_t + * + * Return value: The type of @pattern. See #cairo_pattern_type_t. + **/ +cairo_pattern_type_t +cairo_pattern_get_type (cairo_pattern_t *pattern) +{ + return pattern->type; +} + /** * cairo_pattern_status: * @pattern: a #cairo_pattern_t @@ -641,8 +653,8 @@ cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, if (pattern->status) return; - if (pattern->type != CAIRO_PATTERN_LINEAR && - pattern->type != CAIRO_PATTERN_RADIAL) + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; @@ -689,8 +701,8 @@ cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, if (pattern->status) return; - if (pattern->type != CAIRO_PATTERN_LINEAR && - pattern->type != CAIRO_PATTERN_RADIAL) + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; @@ -894,7 +906,7 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, cairo_status_t status; cairo_bool_t repeat = FALSE; - if (pattern->base.type == CAIRO_PATTERN_LINEAR) + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; @@ -936,7 +948,7 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, return CAIRO_STATUS_SUCCESS; } - if (pattern->base.type == CAIRO_PATTERN_LINEAR) { + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_bool_t is_horizontal; cairo_bool_t is_vertical; @@ -1038,7 +1050,6 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, return CAIRO_STATUS_SUCCESS; } - /** * _cairo_pattern_is_opaque_solid * @@ -1051,11 +1062,11 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, * Return value: %TRUE if the pattern is an opaque, solid color. **/ cairo_bool_t -_cairo_pattern_is_opaque_solid (cairo_pattern_t *pattern) +_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) { cairo_solid_pattern_t *solid; - if (pattern->type != CAIRO_PATTERN_SOLID) + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return FALSE; solid = (cairo_solid_pattern_t *) pattern; @@ -1063,6 +1074,47 @@ _cairo_pattern_is_opaque_solid (cairo_pattern_t *pattern) return CAIRO_ALPHA_IS_OPAQUE (solid->color.alpha); } +static cairo_bool_t +_gradient_is_opaque (const cairo_gradient_pattern_t *gradient) +{ + int i; + + for (i = 0; i < gradient->n_stops; i++) + if (! CAIRO_ALPHA_IS_OPAQUE (gradient->stops[i].color.alpha)) + return FALSE; + + return TRUE; +} + +/** + * _cairo_pattern_is_opaque + * + * Convenience function to determine whether a pattern is an opaque + * pattern (of any type). The same caveats that apply to + * _cairo_pattern_is_opaque_solid apply here as well. + * + * Return value: %TRUE if the pattern is a opaque. + **/ +cairo_bool_t +_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern) +{ + const cairo_pattern_union_t *pattern; + + pattern = (cairo_pattern_union_t *) abstract_pattern; + switch (pattern->base.type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_pattern_is_opaque_solid (abstract_pattern); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_is_opaque (pattern->surface.surface); + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + return _gradient_is_opaque (&pattern->gradient.base); + } + + ASSERT_NOT_REACHED; + return FALSE; +} + static cairo_int_status_t _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, cairo_surface_t *dst, @@ -1151,7 +1203,7 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, } switch (pattern->type) { - case CAIRO_PATTERN_SOLID: { + case CAIRO_PATTERN_TYPE_SOLID: { cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern; status = _cairo_pattern_acquire_surface_for_solid (src, dst, @@ -1159,8 +1211,8 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, surface_out, attributes); } break; - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern; /* fast path for gradients with less than 2 color stops */ @@ -1203,7 +1255,7 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, attributes); } } break; - case CAIRO_PATTERN_SURFACE: { + case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; status = _cairo_pattern_acquire_surface_for_surface (src, dst, @@ -1235,7 +1287,7 @@ _cairo_pattern_release_surface (cairo_pattern_t *pattern, { cairo_surface_pattern_t *surface_pattern; - assert (pattern->type == CAIRO_PATTERN_SURFACE); + assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (cairo_surface_pattern_t *) pattern; _cairo_surface_release_source_image (surface_pattern->surface, @@ -1277,8 +1329,8 @@ _cairo_pattern_acquire_surfaces (cairo_pattern_t *src, /* 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) + if (src->type == CAIRO_PATTERN_TYPE_SOLID && + mask && mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_color_t combined; cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; @@ -1346,7 +1398,7 @@ _cairo_pattern_get_extents (cairo_pattern_t *pattern, cairo_rectangle_t *extents) { if (pattern->extend == CAIRO_EXTEND_NONE && - pattern->type == CAIRO_PATTERN_SURFACE) + pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_status_t status; cairo_rectangle_t surface_extents; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 0c024a26b..68c027151 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -140,6 +140,8 @@ struct cairo_pdf_surface { cairo_array_t alphas; cairo_array_t fonts; cairo_bool_t has_clip; + + cairo_paginated_mode_t paginated_mode; }; #define DEFAULT_DPI 300 @@ -186,6 +188,7 @@ static void _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface); static const cairo_surface_backend_t cairo_pdf_surface_backend; +static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; static unsigned int _cairo_pdf_document_new_object (cairo_pdf_document_t *document) @@ -306,7 +309,8 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *stream, return _cairo_paginated_surface_create (target, CAIRO_CONTENT_COLOR_ALPHA, - width, height); + width, height, + &cairo_pdf_surface_paginated_backend); } /** @@ -333,11 +337,13 @@ cairo_pdf_surface_create_for_stream (cairo_write_func_t write, double width_in_points, double height_in_points) { + cairo_status_t status; cairo_output_stream_t *stream; - stream = _cairo_output_stream_create (write, closure); - if (stream == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); + stream = _cairo_output_stream_create (write, NULL, closure); + status = _cairo_output_stream_get_status (stream); + if (status) { + _cairo_error (status); return (cairo_surface_t*) &_cairo_surface_nil; } @@ -368,11 +374,13 @@ cairo_pdf_surface_create (const char *filename, double width_in_points, double height_in_points) { + cairo_status_t status; cairo_output_stream_t *stream; - stream = _cairo_output_stream_create_for_file (filename); - if (stream == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); + stream = _cairo_output_stream_create_for_filename (filename); + status = _cairo_output_stream_get_status (stream); + if (status) { + _cairo_error (status); return (cairo_surface_t*) &_cairo_surface_nil; } @@ -388,7 +396,7 @@ _cairo_surface_is_pdf (cairo_surface_t *surface) } /** - * cairo__surface_set_dpi: + * cairo_pdf_surface_set_dpi: * @surface: a postscript cairo_surface_t * @x_dpi: horizontal dpi * @y_dpi: vertical dpi @@ -451,6 +459,8 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_resource_t)); surface->has_clip = FALSE; + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + return &surface->base; } @@ -654,8 +664,10 @@ emit_image_rgb_data (cairo_pdf_document_t *document, opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, image->width, image->height); - if (opaque->status) + if (opaque->status) { + free (rgb); return 0; + } _cairo_pattern_init_for_surface (&pattern.surface, &image->base); @@ -841,7 +853,7 @@ _cairo_pdf_surface_composite (cairo_operator_t op, if (mask_pattern) return CAIRO_STATUS_SUCCESS; - if (src_pattern->type != CAIRO_PATTERN_SURFACE) + if (src_pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_STATUS_SUCCESS; if (src->surface->backend == &cairo_pdf_surface_backend) @@ -1237,16 +1249,16 @@ static cairo_status_t emit_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) { switch (pattern->type) { - case CAIRO_PATTERN_SOLID: + case CAIRO_PATTERN_TYPE_SOLID: return emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); - case CAIRO_PATTERN_SURFACE: + case CAIRO_PATTERN_TYPE_SURFACE: return emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); - case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_TYPE_LINEAR: return emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); - case CAIRO_PATTERN_RADIAL: + case CAIRO_PATTERN_TYPE_RADIAL: return emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); } @@ -1320,55 +1332,6 @@ _cairo_pdf_path_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_pdf_surface_fill (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *pattern, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_document_t *document = surface->document; - const char *pdf_operator; - cairo_status_t status; - - status = emit_pattern (surface, pattern); - if (status) - return status; - - /* 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); - - 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, - document->output_stream); - - switch (fill_rule) { - case CAIRO_FILL_RULE_WINDING: - pdf_operator = "f"; - break; - case CAIRO_FILL_RULE_EVEN_ODD: - pdf_operator = "f*"; - break; - default: - ASSERT_NOT_REACHED; - } - - _cairo_output_stream_printf (document->output_stream, - "%s\r\n", - pdf_operator); - - return status; -} - static cairo_int_status_t _cairo_pdf_surface_composite_trapezoids (cairo_operator_t op, cairo_pattern_t *pattern, @@ -1632,38 +1595,6 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface, cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); } -static const cairo_surface_backend_t cairo_pdf_surface_backend = { - _cairo_pdf_surface_create_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, - NULL, /* set_clip_region */ - _cairo_pdf_surface_intersect_clip_path, - _cairo_pdf_surface_get_extents, - _cairo_pdf_surface_old_show_glyphs, - _cairo_pdf_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - /* Here are the drawing functions */ - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - _cairo_pdf_surface_fill, - NULL /* show_glyphs */ -}; - static cairo_pdf_document_t * _cairo_pdf_document_create (cairo_output_stream_t *output_stream, double width, @@ -2086,3 +2017,229 @@ _cairo_pdf_document_add_page (cairo_pdf_document_t *document, return CAIRO_STATUS_SUCCESS; } + +static cairo_bool_t +_surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (pattern->surface->backend->acquire_source_image != NULL) + return TRUE; + + return FALSE; +} + +static cairo_bool_t +_pattern_supported (const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + return _surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); + + return FALSE; +} + +static cairo_int_status_t +_operation_supported (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (! _pattern_supported (pattern)) + return FALSE; + + if (_cairo_operator_always_opaque (op)) + return TRUE; + + if (_cairo_operator_always_translucent (op)) + return FALSE; + + return _cairo_pattern_is_opaque (pattern); +} + +static cairo_int_status_t +_analyze_operation (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (_operation_supported (surface, op, pattern)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_pdf_surface_paint (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* One would think that since we analyzed this away as unsupported + * that it would never be called after analyzing. But in fact, + * paint is called to paint the actual fallback surface. So we + * must not ASSERT_NOT_REACHED as we do for the other drawing + * operations. */ + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_pdf_surface_mask (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_pattern_t *mask) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + ASSERT_NOT_REACHED; + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_pdf_surface_stroke (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + ASSERT_NOT_REACHED; + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_pdf_surface_fill (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_document_t *document = surface->document; + const char *pdf_operator; + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _analyze_operation (surface, op, source); + + assert (_operation_supported (surface, op, source)); + + status = emit_pattern (surface, source); + if (status) + return status; + + /* After emitting the pattern 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); + + 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, + document->output_stream); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + pdf_operator = "f"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + pdf_operator = "f*"; + break; + default: + ASSERT_NOT_REACHED; + } + + _cairo_output_stream_printf (document->output_stream, + "%s\r\n", + pdf_operator); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + ASSERT_NOT_REACHED; + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_cairo_pdf_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; +} + +static const cairo_surface_backend_t cairo_pdf_surface_backend = { + CAIRO_SURFACE_TYPE_PDF, + _cairo_pdf_surface_create_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, + NULL, /* set_clip_region */ + _cairo_pdf_surface_intersect_clip_path, + _cairo_pdf_surface_get_extents, + _cairo_pdf_surface_old_show_glyphs, + _cairo_pdf_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here are the drawing functions */ + + _cairo_pdf_surface_paint, + _cairo_pdf_surface_mask, + _cairo_pdf_surface_stroke, + _cairo_pdf_surface_fill, + _cairo_pdf_surface_show_glyphs, + NULL, /* snapshot */ +}; + +static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend = { + NULL, /* start_page */ + _cairo_pdf_surface_set_paginated_mode +}; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 37053e965..29b6882b8 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -41,6 +41,7 @@ #include "cairo-ps.h" #include "cairo-font-subset-private.h" #include "cairo-paginated-surface-private.h" +#include "cairo-meta-surface-private.h" #include "cairo-ft-private.h" #include @@ -50,17 +51,44 @@ * * - Add document structure convention comments where appropriate. * - * - Fix image compression. - * * - Create a set of procs to use... specifically a trapezoid proc. */ static const cairo_surface_backend_t cairo_ps_surface_backend; +static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; + +/* + * Type1 and Type3 PS fonts can hold only 256 glyphs. + * + * XXX Work around this by placing each set of 256 glyphs in a separate + * font. No separate data structure is kept for this; the font name is + * generated from all but the low 8 bits of the output glyph id. + */ + +typedef struct cairo_ps_glyph { + cairo_hash_entry_t base; /* font glyph index */ + unsigned int output_glyph; /* PS sub-font glyph index */ +} cairo_ps_glyph_t; + +typedef struct cairo_ps_font { + cairo_hash_entry_t base; + cairo_scaled_font_t *scaled_font; + unsigned int output_font; + cairo_hash_table_t *glyphs; + unsigned int max_glyph; +} cairo_ps_font_t; typedef struct cairo_ps_surface { cairo_surface_t base; - /* PS-specific fields */ + /* Here final_stream corresponds to the stream/file passed to + * cairo_ps_surface_create surface is built. Meanwhile stream is a + * temporary stream in which the file output is built, (so that + * the header can be built and inserted into the target stream + * before the contents of the temporary stream are copied). */ + cairo_output_stream_t *final_stream; + + FILE *tmpfile; cairo_output_stream_t *stream; double width; @@ -68,988 +96,17 @@ typedef struct cairo_ps_surface { double x_dpi; double y_dpi; - cairo_bool_t need_start_page; int num_pages; -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED - cairo_array_t fonts; -#endif + cairo_paginated_mode_t paginated_mode; + + cairo_hash_table_t *fonts; + unsigned int max_font; + } cairo_ps_surface_t; #define PS_SURFACE_DPI_DEFAULT 300.0 -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED -static cairo_int_status_t -_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface); -#endif - -static void -_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) -{ - time_t now; - - now = time (NULL); - - _cairo_output_stream_printf (surface->stream, - "%%!PS-Adobe-3.0\n" - "%%%%Creator: cairo (http://cairographics.org)\n" - "%%%%CreationDate: %s" - "%%%%Pages: (atend)\n" - "%%%%BoundingBox: %f %f %f %f\n", - ctime (&now), - 0.0, 0.0, - surface->width, - surface->height); - - /* The "/FlateDecode filter" currently used is a feature of - * LanguageLevel 3 */ - _cairo_output_stream_printf (surface->stream, - "%%%%DocumentData: Binary\n" - "%%%%LanguageLevel: 3\n" - "%%%%Orientation: Portrait\n" - "%%%%EndComments\n"); -} - -static void -_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) -{ - _cairo_output_stream_printf (surface->stream, - "%%%%Trailer\n" - "%%%%Pages: %d\n" - "%%%%EOF\n", - surface->num_pages); -} - -static cairo_surface_t * -_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, - double width, - double height) -{ - cairo_ps_surface_t *surface; - - surface = malloc (sizeof (cairo_ps_surface_t)); - if (surface == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; - } - - _cairo_surface_init (&surface->base, &cairo_ps_surface_backend); - - surface->stream = stream; - - surface->width = width; - surface->height = height; - surface->x_dpi = PS_SURFACE_DPI_DEFAULT; - surface->y_dpi = PS_SURFACE_DPI_DEFAULT; -#if DONE_ADDING_DEVICE_SCALE_SUPPORT_AFTER_SWITCHING_TO_PAGINATED - surface->base.device_x_scale = surface->x_dpi / 72.0; - surface->base.device_y_scale = surface->y_dpi / 72.0; -#endif - - surface->need_start_page = TRUE; - surface->num_pages = 0; - -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED - _cairo_array_init (&surface->fonts, sizeof (cairo_font_subset_t *)); -#endif - - _cairo_ps_surface_emit_header (surface); - - return _cairo_paginated_surface_create (&surface->base, - CAIRO_CONTENT_COLOR_ALPHA, - width, height); -} - -/** - * cairo_ps_surface_create: - * @filename: a filename for the PS output (must be writable) - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a PostScript surface of the specified size in points to be - * written to @filename. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - **/ -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) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; - } - - return _cairo_ps_surface_create_for_stream_internal (stream, - width_in_points, - height_in_points); -} - -/** - * cairo_ps_surface_create_for_stream: - * @write: a #cairo_write_func_t to accept the output data - * @closure: the closure argument for @write - * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) - * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) - * - * Creates a PostScript surface of the specified size in points to be - * written incrementally to the stream represented by @write and - * @closure. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - */ -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) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; - } - - return _cairo_ps_surface_create_for_stream_internal (stream, - width_in_points, - height_in_points); -} - -static cairo_bool_t -_cairo_surface_is_ps (cairo_surface_t *surface) -{ - return surface->backend == &cairo_ps_surface_backend; -} - -/** - * cairo_ps_surface_set_dpi: - * @surface: a postscript cairo_surface_t - * @x_dpi: horizontal dpi - * @y_dpi: vertical dpi - * - * Set the horizontal and vertical resolution for image fallbacks. - * When the ps backend needs to fall back to image overlays, it will - * use this resolution. These DPI values are not used for any other - * purpose, (in particular, they do not have any bearing on the size - * passed to cairo_ps_surface_create() nor on the CTM). - **/ -void -cairo_ps_surface_set_dpi (cairo_surface_t *surface, - double x_dpi, - double y_dpi) -{ - cairo_surface_t *target; - cairo_ps_surface_t *ps_surface; - - if (! _cairo_surface_is_paginated (surface)) { - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return; - } - - target = _cairo_paginated_surface_get_target (surface); - - if (! _cairo_surface_is_ps (surface)) { - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return; - } - - ps_surface = (cairo_ps_surface_t *) target; - - ps_surface->x_dpi = x_dpi; - ps_surface->y_dpi = y_dpi; - -#if DONE_ADDING_DEVICE_SCALE_SUPPORT_AFTER_SWITCHING_TO_PAGINATED - ps_surface->base.device_x_scale = ps_surface->x_dpi / 72.0; - ps_surface->base.device_y_scale = ps_surface->y_dpi / 72.0; -#endif -} - -/* XXX */ -static cairo_status_t -_cairo_ps_surface_finish (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED - _cairo_ps_surface_write_font_subsets (surface); -#endif - - _cairo_ps_surface_emit_footer (surface); - -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED - for (i = 0; i < surface->fonts.num_elements; i++) { - _cairo_array_copy_element (&surface->fonts, i, &subset); - _cairo_font_subset_destroy (subset); - } - _cairo_array_fini (&surface->fonts); -#endif - - _cairo_output_stream_destroy (surface->stream); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_ps_surface_start_page (cairo_ps_surface_t *surface) -{ - _cairo_output_stream_printf (surface->stream, - "%%%%Page: %d\n", - ++surface->num_pages); - - - _cairo_output_stream_printf (surface->stream, - "gsave %f %f translate %f %f scale \n", - 0.0, surface->height, - 1.0/surface->base.device_x_scale, - -1.0/surface->base.device_y_scale); - - surface->need_start_page = FALSE; -} - -static void -_cairo_ps_surface_end_page (cairo_ps_surface_t *surface) -{ - _cairo_output_stream_printf (surface->stream, - "grestore\n"); - - surface->need_start_page = TRUE; -} - -static cairo_int_status_t -_cairo_ps_surface_copy_page (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - - _cairo_ps_surface_end_page (surface); - - _cairo_output_stream_printf (surface->stream, "copypage\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_ps_surface_show_page (void *abstract_surface) -{ - cairo_ps_surface_t *surface = abstract_surface; - - _cairo_ps_surface_end_page (surface); - - _cairo_output_stream_printf (surface->stream, "showpage\n"); - - surface->need_start_page = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED -static cairo_font_subset_t * -_cairo_ps_surface_get_font (cairo_ps_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_status_t status; - cairo_unscaled_font_t *unscaled_font; - cairo_font_subset_t *subset; - unsigned int num_fonts, i; - - /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ - if (! _cairo_scaled_font_is_ft (scaled_font)) - return NULL; - - /* XXX Why is this an ft specific function? */ - unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font); - - num_fonts = _cairo_array_num_elements (&surface->fonts); - for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&surface->fonts, i, &subset); - if (subset->unscaled_font == unscaled_font) - return subset; - } - - subset = _cairo_font_subset_create (unscaled_font); - if (subset == NULL) - return NULL; - - subset->font_id = surface->fonts.num_elements; - - status = _cairo_array_append (&surface->fonts, &subset); - if (status) { - _cairo_font_subset_destroy (subset); - return NULL; - } - - return subset; -} - -static cairo_int_status_t -_cairo_ps_surface_write_type42_dict (cairo_ps_surface_t *surface, - cairo_font_subset_t *subset) -{ - const char *data; - unsigned long data_size; - cairo_status_t status; - int i; - - status = CAIRO_STATUS_SUCCESS; - - /* FIXME: Figure out document structure convention for fonts */ - - _cairo_output_stream_printf (surface->stream, - "11 dict begin\n" - "/FontType 42 def\n" - "/FontName /f%d def\n" - "/PaintType 0 def\n" - "/FontMatrix [ 1 0 0 1 0 0 ] def\n" - "/FontBBox [ 0 0 0 0 ] def\n" - "/Encoding 256 array def\n" - "0 1 255 { Encoding exch /.notdef put } for\n", - subset->font_id); - - /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ - - for (i = 1; i < subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->stream, - "Encoding %d /g%d put\n", i, i); - - _cairo_output_stream_printf (surface->stream, - "/CharStrings %d dict dup begin\n" - "/.notdef 0 def\n", - subset->num_glyphs); - - for (i = 1; i < subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->stream, - "/g%d %d def\n", i, i); - - _cairo_output_stream_printf (surface->stream, - "end readonly def\n"); - - status = _cairo_font_subset_generate (subset, &data, &data_size); - - /* FIXME: We need to break up fonts bigger than 64k so we don't - * exceed string size limitation. At glyph boundaries. Stupid - * postscript. */ - _cairo_output_stream_printf (surface->stream, - "/sfnts [<"); - - _cairo_output_stream_write_hex_string (surface->stream, data, data_size); - - _cairo_output_stream_printf (surface->stream, - ">] def\n" - "FontName currentdict end definefont pop\n"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface) -{ - cairo_font_subset_t *subset; - int i; - - for (i = 0; i < surface->fonts.num_elements; i++) { - _cairo_array_copy_element (&surface->fonts, i, &subset); - _cairo_ps_surface_write_type42_dict (surface, subset); - } - - return CAIRO_STATUS_SUCCESS; -} -#endif - -/* XXX: This function wil go away in favor of the new "analysis mode" - * of cairo_paginated_surface_t */ -static cairo_int_status_t -_cairo_ps_surface_add_fallback_area (cairo_ps_surface_t *surface, - int x, int y, - unsigned int width, - unsigned int height) -{ - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -color_is_gray (cairo_color_t *color) -{ - const double epsilon = 0.00001; - - return (fabs (color->red - color->green) < epsilon && - fabs (color->red - color->blue) < epsilon); -} - -static cairo_bool_t -color_is_translucent (const cairo_color_t *color) -{ - return color->alpha < 0.999; -} - -static cairo_bool_t -format_is_translucent (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_ARGB32: - return TRUE; - case CAIRO_FORMAT_RGB24: - return FALSE; - case CAIRO_FORMAT_A8: - return TRUE; - case CAIRO_FORMAT_A1: - return TRUE; - } - return TRUE; -} - -static cairo_bool_t -surface_is_translucent (const cairo_surface_t *surface) -{ - if (_cairo_surface_is_image (surface)) { - const cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; - - return format_is_translucent (image_surface->format); - } - return TRUE; -} - -static cairo_bool_t -gradient_is_translucent (const cairo_gradient_pattern_t *gradient) -{ - return TRUE; /* XXX no gradient support */ -#if 0 - int i; - - for (i = 0; i < gradient->n_stops; i++) - if (color_is_translucent (&gradient->stops[i].color)) - return TRUE; - return FALSE; -#endif -} - -static cairo_bool_t -pattern_is_translucent (const cairo_pattern_t *abstract_pattern) -{ - const cairo_pattern_union_t *pattern; - - pattern = (cairo_pattern_union_t *) abstract_pattern; - switch (pattern->base.type) { - case CAIRO_PATTERN_SOLID: - return color_is_translucent (&pattern->solid.color); - case CAIRO_PATTERN_SURFACE: - return surface_is_translucent (pattern->surface.surface); - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: - return gradient_is_translucent (&pattern->gradient.base); - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -static cairo_bool_t -operator_always_opaque (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - - case CAIRO_OPERATOR_SOURCE: - return TRUE; - - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - return FALSE; - - case CAIRO_OPERATOR_DEST: - return TRUE; - - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - return FALSE; - - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - return FALSE; - } - return FALSE; -} - -static cairo_bool_t -operator_always_translucent (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - - case CAIRO_OPERATOR_SOURCE: - return FALSE; - - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - return FALSE; - - case CAIRO_OPERATOR_DEST: - return FALSE; - - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - return FALSE; - - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - return TRUE; - } - return TRUE; -} - -static cairo_bool_t -color_operation_needs_fallback (cairo_operator_t op, - const cairo_color_t *color) -{ - if (operator_always_opaque (op)) - return FALSE; - if (operator_always_translucent (op)) - return TRUE; - return color_is_translucent (color); -} - -static cairo_bool_t -pattern_type_supported (const cairo_pattern_t *pattern) -{ - if (pattern->type == CAIRO_PATTERN_SOLID) - return TRUE; - return FALSE; -} - -static cairo_bool_t -pattern_operation_needs_fallback (cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - if (! pattern_type_supported (pattern)) - return TRUE; - if (operator_always_opaque (op)) - return FALSE; - if (operator_always_translucent (op)) - return TRUE; - return pattern_is_translucent (pattern); -} - -/* PS Output - this section handles output of the parts of the meta - * surface we can render natively in PS. */ - -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 cairo_status_t -emit_image (cairo_ps_surface_t *surface, - cairo_image_surface_t *image, - cairo_matrix_t *matrix) -{ - cairo_status_t status; - unsigned char *rgb, *compressed; - unsigned long rgb_size, compressed_size; - cairo_surface_t *opaque; - cairo_image_surface_t *opaque_image; - cairo_pattern_union_t pattern; - cairo_matrix_t d2i; - int x, y, i; - - /* PostScript can not represent the alpha channel, so we blend the - current image over a white RGB surface to eliminate it. */ - - if (image->base.status) - return image->base.status; - - if (image->format != CAIRO_FORMAT_RGB24) { - opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - image->width, - image->height); - if (opaque->status) { - status = CAIRO_STATUS_NO_MEMORY; - goto bail0; - } - - _cairo_pattern_init_for_surface (&pattern.surface, &image->base); - - _cairo_surface_fill_rectangle (opaque, - CAIRO_OPERATOR_SOURCE, - CAIRO_COLOR_WHITE, - 0, 0, image->width, image->height); - - _cairo_surface_composite (CAIRO_OPERATOR_OVER, - &pattern.base, - NULL, - opaque, - 0, 0, - 0, 0, - 0, 0, - image->width, - image->height); - - _cairo_pattern_fini (&pattern.base); - opaque_image = (cairo_image_surface_t *) opaque; - } else { - opaque = &image->base; - opaque_image = image; - } - - rgb_size = 3 * opaque_image->width * opaque_image->height; - rgb = malloc (rgb_size); - if (rgb == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto bail1; - } - - i = 0; - for (y = 0; y < opaque_image->height; y++) { - pixman_bits_t *pixel = (pixman_bits_t *) (opaque_image->data + y * opaque_image->stride); - for (x = 0; x < opaque_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) { - status = CAIRO_STATUS_NO_MEMORY; - goto bail2; - } - - /* matrix transforms from user space to image space. We need to - * transform from device space to image space to compensate for - * postscripts coordinate system. */ - cairo_matrix_init (&d2i, 1, 0, 0, 1, 0, 0); - cairo_matrix_multiply (&d2i, &d2i, matrix); - - _cairo_output_stream_printf (surface->stream, - "/DeviceRGB setcolorspace\n" - "<<\n" - " /ImageType 1\n" - " /Width %d\n" - " /Height %d\n" - " /BitsPerComponent 8\n" - " /Decode [ 0 1 0 1 0 1 ]\n" - " /DataSource currentfile\n" - " /ImageMatrix [ %f %f %f %f %f %f ]\n" - ">>\n" - "image\n", - opaque_image->width, - opaque_image->height, - d2i.xx, d2i.yx, - d2i.xy, d2i.yy, - d2i.x0, d2i.y0); - - /* Compressed image data */ - _cairo_output_stream_write (surface->stream, rgb, rgb_size); - status = CAIRO_STATUS_SUCCESS; - - _cairo_output_stream_printf (surface->stream, - "\n"); - - free (compressed); - bail2: - free (rgb); - bail1: - if (opaque_image != image) - cairo_surface_destroy (opaque); - bail0: - return status; -} - -static void -emit_solid_pattern (cairo_ps_surface_t *surface, - cairo_solid_pattern_t *pattern) -{ - if (color_is_gray (&pattern->color)) - _cairo_output_stream_printf (surface->stream, - "%f setgray\n", - pattern->color.red); - else - _cairo_output_stream_printf (surface->stream, - "%f %f %f setrgbcolor\n", - pattern->color.red, - pattern->color.green, - pattern->color.blue); -} - -static void -emit_surface_pattern (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - /* XXX: NYI */ -} - -static void -emit_linear_pattern (cairo_ps_surface_t *surface, - cairo_linear_pattern_t *pattern) -{ - /* XXX: NYI */ -} - -static void -emit_radial_pattern (cairo_ps_surface_t *surface, - cairo_radial_pattern_t *pattern) -{ - /* XXX: NYI */ -} - -static void -emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern) -{ - /* FIXME: We should keep track of what pattern is currently set in - * the postscript file and only emit code if we're setting a - * different 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 cairo_int_status_t -_cairo_ps_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_ps_surface_t *surface = abstract_dst; - cairo_output_stream_t *stream = surface->stream; - cairo_surface_pattern_t *surface_pattern; - cairo_status_t status; - cairo_image_surface_t *image; - void *image_extra; - - if (surface->need_start_page) - _cairo_ps_surface_start_page (surface); - - if (mask_pattern) { - /* FIXME: Investigate how this can be done... we'll probably - * need pixmap fallbacks for this, though. */ - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_composite: with mask\n"); - goto bail; - } - - status = CAIRO_STATUS_SUCCESS; - switch (src_pattern->type) { - case CAIRO_PATTERN_SOLID: - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_composite: solid\n"); - goto bail; - - case CAIRO_PATTERN_SURFACE: - surface_pattern = (cairo_surface_pattern_t *) src_pattern; - - if (src_pattern->extend != CAIRO_EXTEND_NONE) { - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_composite: repeating image\n"); - goto bail; - } - - - status = _cairo_surface_acquire_source_image (surface_pattern->surface, - &image, - &image_extra); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_composite: src_pattern not available as image\n"); - goto bail; - } else if (status) { - break; - } - status = emit_image (surface, image, &src_pattern->matrix); - _cairo_surface_release_source_image (surface_pattern->surface, - image, image_extra); - break; - - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_composite: gradient\n"); - goto bail; - } - - return status; -bail: - return _cairo_ps_surface_add_fallback_area (surface, dst_x, dst_y, width, height); -} - -static cairo_int_status_t -_cairo_ps_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_ps_surface_t *surface = abstract_surface; - cairo_output_stream_t *stream = surface->stream; - cairo_solid_pattern_t solid; - int i; - - if (!num_rects) - return CAIRO_STATUS_SUCCESS; - - if (surface->need_start_page) - _cairo_ps_surface_start_page (surface); - - if (color_operation_needs_fallback (op, color)) { - int min_x = rects[0].x; - int min_y = rects[0].y; - int max_x = rects[0].x + rects[0].width; - int max_y = rects[0].y + rects[0].height; - - for (i = 1; i < num_rects; i++) { - if (rects[i].x < min_x) min_x = rects[i].x; - if (rects[i].y < min_y) min_y = rects[i].y; - if (rects[i].x + rects[i].width > max_x) max_x = rects[i].x + rects[i].width; - if (rects[i].y + rects[i].height > max_y) max_y = rects[i].y + rects[i].height; - } - return _cairo_ps_surface_add_fallback_area (surface, min_x, min_y, max_x - min_x, max_y - min_y); - } - - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_fill_rectangles\n"); - - _cairo_pattern_init_solid (&solid, color); - emit_pattern (surface, &solid.base); - _cairo_pattern_fini (&solid.base); - - _cairo_output_stream_printf (stream, "["); - for (i = 0; i < num_rects; i++) { - _cairo_output_stream_printf (stream, - " %d %d %d %d", - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - } - - _cairo_output_stream_printf (stream, " ] rectfill\n"); - - return CAIRO_STATUS_SUCCESS; -} - -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_ps_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - 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_ps_surface_t *surface = abstract_dst; - cairo_output_stream_t *stream = surface->stream; - int i; - - if (pattern_operation_needs_fallback (op, pattern)) - return _cairo_ps_surface_add_fallback_area (surface, x_dst, y_dst, width, height); - - if (surface->need_start_page) - _cairo_ps_surface_start_page (surface); - - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_composite_trapezoids\n"); - - emit_pattern (surface, pattern); - - for (i = 0; i < num_traps; i++) { - double left_x1, left_x2, right_x1, right_x2, top, bottom; - - 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); - top = _cairo_fixed_to_double (traps[i].top); - bottom = _cairo_fixed_to_double (traps[i].bottom); - - _cairo_output_stream_printf - (stream, - "%f %f moveto %f %f lineto %f %f lineto %f %f lineto " - "closepath\n", - left_x1, top, - left_x2, bottom, - right_x2, bottom, - right_x1, top); - } - - _cairo_output_stream_printf (stream, - "fill\n"); - - return CAIRO_STATUS_SUCCESS; -} - - static cairo_status_t _cairo_ps_surface_path_move_to (void *closure, cairo_point_t *point) { @@ -1107,6 +164,1126 @@ _cairo_ps_surface_path_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } +static void +_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) +{ + time_t now; + + now = time (NULL); + + _cairo_output_stream_printf (surface->final_stream, + "%%!PS-Adobe-3.0\n" + "%%%%Creator: cairo (http://cairographics.org)\n" + "%%%%CreationDate: %s" + "%%%%Pages: %d\n" + "%%%%BoundingBox: %f %f %f %f\n", + ctime (&now), + surface->num_pages, + 0.0, 0.0, + surface->width, + surface->height); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%DocumentData: Clean7Bit\n" + "%%%%LanguageLevel: 2\n" + "%%%%Orientation: Portrait\n" + "%%%%EndComments\n"); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginProlog\n" + "/C{curveto}bind def\n" + "/F{fill}bind def\n" + "/G{setgray}bind def\n" + "/L{lineto}bind def\n" + "/M{moveto}bind def\n" + "/P{closepath}bind def\n" + "/R{setrgbcolor}bind def\n" + "/S{show}bind def\n" + "%%%%EndProlog\n"); +} + +static cairo_bool_t +_cairo_ps_glyph_equal (const void *key_a, const void *key_b) +{ + const cairo_ps_glyph_t *ps_glyph_a = key_a; + const cairo_ps_glyph_t *ps_glyph_b = key_b; + + return ps_glyph_a->base.hash == ps_glyph_b->base.hash; +} + +static void +_cairo_ps_glyph_key_init (cairo_ps_glyph_t *ps_glyph, + unsigned long index) +{ + ps_glyph->base.hash = index; +} + +static cairo_ps_glyph_t * +_cairo_ps_glyph_create (cairo_ps_font_t *ps_font, + unsigned long index) +{ + cairo_ps_glyph_t *ps_glyph = malloc (sizeof (cairo_ps_glyph_t)); + + if (!ps_glyph) + return NULL; + _cairo_ps_glyph_key_init (ps_glyph, index); + ps_glyph->output_glyph = ps_font->max_glyph++; + return ps_glyph; +} + +static void +_cairo_ps_glyph_destroy (cairo_ps_glyph_t *ps_glyph) +{ + free (ps_glyph); +} + +static cairo_status_t +_cairo_ps_glyph_find (cairo_ps_font_t *font, + cairo_scaled_font_t *scaled_font, + unsigned long index, + cairo_ps_glyph_t **result) +{ + cairo_ps_glyph_t key; + cairo_ps_glyph_t *ps_glyph; + cairo_status_t status; + + _cairo_ps_glyph_key_init (&key, index); + if (!_cairo_hash_table_lookup (font->glyphs, + &key.base, + (cairo_hash_entry_t **) &ps_glyph)) { + ps_glyph = _cairo_ps_glyph_create (font, index); + if (!ps_glyph) + return CAIRO_STATUS_NO_MEMORY; + status = _cairo_hash_table_insert (font->glyphs, &ps_glyph->base); + if (status) + return status; + } + *result = ps_glyph; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_ps_font_equal (const void *key_a, const void *key_b) +{ + const cairo_ps_font_t *ps_font_a = key_a; + const cairo_ps_font_t *ps_font_b = key_b; + + return ps_font_a->scaled_font == ps_font_b->scaled_font; +} + +static void +_cairo_ps_font_key_init (cairo_ps_font_t *ps_font, + cairo_scaled_font_t *scaled_font) +{ + ps_font->base.hash = (unsigned long) scaled_font; + ps_font->scaled_font = scaled_font; +} + +static cairo_ps_font_t * +_cairo_ps_font_create (cairo_ps_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_ps_font_t *ps_font = malloc (sizeof (cairo_ps_font_t)); + if (!ps_font) + return NULL; + _cairo_ps_font_key_init (ps_font, scaled_font); + ps_font->glyphs = _cairo_hash_table_create (_cairo_ps_glyph_equal); + if (!ps_font->glyphs) { + free (ps_font); + return NULL; + } + ps_font->max_glyph = 0; + ps_font->output_font = surface->max_font++; + cairo_scaled_font_reference (ps_font->scaled_font); + return ps_font; +} + +static void +_cairo_ps_font_destroy_glyph (void *entry, void *closure) +{ + cairo_ps_glyph_t *ps_glyph = entry; + cairo_ps_font_t *ps_font = closure; + + _cairo_hash_table_remove (ps_font->glyphs, &ps_glyph->base); + _cairo_ps_glyph_destroy (ps_glyph); +} + +static void +_cairo_ps_font_destroy (cairo_ps_font_t *ps_font) +{ + _cairo_hash_table_foreach (ps_font->glyphs, + _cairo_ps_font_destroy_glyph, + ps_font); + _cairo_hash_table_destroy (ps_font->glyphs); + cairo_scaled_font_destroy (ps_font->scaled_font); + free (ps_font); +} + +static void +_cairo_ps_surface_destroy_font (cairo_ps_surface_t *surface, + cairo_ps_font_t *ps_font) +{ + _cairo_hash_table_remove (surface->fonts, &ps_font->base); + _cairo_ps_font_destroy (ps_font); +} + +static cairo_status_t +_cairo_ps_font_find (cairo_ps_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_ps_font_t **result) +{ + cairo_ps_font_t key; + cairo_ps_font_t *ps_font; + cairo_status_t status; + + _cairo_ps_font_key_init (&key, scaled_font); + if (!_cairo_hash_table_lookup (surface->fonts, &key.base, + (cairo_hash_entry_t **) &ps_font)) + { + ps_font = _cairo_ps_font_create (surface, scaled_font); + if (!ps_font) + return CAIRO_STATUS_NO_MEMORY; + status = _cairo_hash_table_insert (surface->fonts, + &ps_font->base); + if (status) + return status; + } + *result = ps_font; + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _cairo_ps_font_glyph_select { + cairo_ps_glyph_t **glyphs; + int subfont; + int numglyph; +} cairo_ps_font_glyph_select_t; + +static void +_cairo_ps_font_select_glyphs (void *entry, void *closure) +{ + cairo_ps_glyph_t *ps_glyph = entry; + cairo_ps_font_glyph_select_t *ps_glyph_select = closure; + + if (ps_glyph->output_glyph >> 8 == ps_glyph_select->subfont) { + unsigned long sub_glyph = ps_glyph->output_glyph & 0xff; + ps_glyph_select->glyphs[sub_glyph] = ps_glyph; + if (sub_glyph >= ps_glyph_select->numglyph) + ps_glyph_select->numglyph = sub_glyph + 1; + } +} + +static cairo_status_t +_cairo_ps_surface_emit_glyph (cairo_ps_surface_t *surface, + cairo_ps_font_t *ps_font, + cairo_ps_glyph_t *ps_glyph) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + _cairo_output_stream_printf (surface->final_stream, + "\t\t{ %% %d\n", ps_glyph->output_glyph); + status = _cairo_scaled_glyph_lookup (ps_font->scaled_font, + ps_glyph->base.hash, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + /* + * If that fails, try again but ask for an image instead + */ + if (status) + status = _cairo_scaled_glyph_lookup (ps_font->scaled_font, + ps_glyph->base.hash, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (status) { + _cairo_output_stream_printf (surface->final_stream, "\t\t}\n"); + return status; + } + _cairo_output_stream_printf (surface->final_stream, + "%f %f %f %f 0 0 setcachedevice\n", + _cairo_fixed_to_double (scaled_glyph->bbox.p1.x), + -_cairo_fixed_to_double (scaled_glyph->bbox.p2.y), + _cairo_fixed_to_double (scaled_glyph->bbox.p2.x), + -_cairo_fixed_to_double (scaled_glyph->bbox.p1.y)); + + status = _cairo_path_fixed_interpret (scaled_glyph->path, + CAIRO_DIRECTION_FORWARD, + _cairo_ps_surface_path_move_to, + _cairo_ps_surface_path_line_to, + _cairo_ps_surface_path_curve_to, + _cairo_ps_surface_path_close_path, + surface->final_stream); + + _cairo_output_stream_printf (surface->final_stream, + "F\n"); + + _cairo_output_stream_printf (surface->final_stream, + "\t\t}\n"); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_ps_surface_emit_font (void *entry, void *closure) +{ + cairo_ps_font_t *ps_font = entry; + cairo_ps_surface_t *surface = closure; + cairo_ps_font_glyph_select_t glyph_select; + cairo_ps_glyph_t *ps_glyphs[256], *ps_glyph; + int glyph, numglyph; + int subfont, nsubfont; + + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_font\n"); + nsubfont = (ps_font->max_glyph >> 8) + 1; + for (subfont = 0; subfont < nsubfont; subfont++) { + _cairo_output_stream_printf (surface->final_stream, + "/CairoFont-%d-%d <<\n", + ps_font->output_font, + subfont); + memset (ps_glyphs, '\0', sizeof (ps_glyphs)); + glyph_select.glyphs = ps_glyphs; + glyph_select.numglyph = 0; + glyph_select.subfont = subfont; + _cairo_hash_table_foreach (ps_font->glyphs, + _cairo_ps_font_select_glyphs, + &glyph_select); + _cairo_output_stream_printf (surface->final_stream, + "\t/FontType\t3\n" + "\t/FontMatrix\t[1 0 0 1 0 0]\n" + "\t/Encoding\t[0]\n" + "\t/FontBBox\t[0 0 10 10]\n" + "\t/Glyphs [\n"); + numglyph = glyph_select.numglyph; + for (glyph = 0; glyph < numglyph; glyph++) { + ps_glyph = ps_glyphs[glyph]; + if (ps_glyph) { + _cairo_ps_surface_emit_glyph (surface, + ps_font, + ps_glyph); + } else { + _cairo_output_stream_printf (surface->final_stream, + "\t\t{ } %% %d\n", glyph); + } + _cairo_ps_font_destroy_glyph (ps_glyph, ps_font); + } + _cairo_output_stream_printf (surface->final_stream, + "\t]\n" + "\t/BuildChar {\n" + "\t\texch /Glyphs get\n" + "\t\texch get exec\n" + "\t}\n" + ">> definefont pop\n"); + } + _cairo_ps_surface_destroy_font (surface, ps_font); +} + + +static void +_cairo_ps_surface_emit_fonts (cairo_ps_surface_t *surface) +{ + _cairo_hash_table_foreach (surface->fonts, + _cairo_ps_surface_emit_font, + surface); + _cairo_hash_table_destroy (surface->fonts); + surface->fonts = NULL; +} + +static void +_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface) +{ + char buf[4096]; + int n; + + rewind (surface->tmpfile); + while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0) + _cairo_output_stream_write (surface->final_stream, buf, n); +} + +static void +_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) +{ + _cairo_output_stream_printf (surface->final_stream, + "%%%%Trailer\n" + "%%%%EOF\n"); +} + +static cairo_surface_t * +_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height) +{ + cairo_status_t status; + cairo_ps_surface_t *surface; + + surface = malloc (sizeof (cairo_ps_surface_t)); + if (surface == NULL) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + _cairo_surface_init (&surface->base, &cairo_ps_surface_backend); + + surface->final_stream = stream; + + surface->tmpfile = tmpfile (); + surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); + status = _cairo_output_stream_get_status (surface->stream); + if (status) { + fclose (surface->tmpfile); + free (surface); + _cairo_error (status); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + surface->fonts = _cairo_hash_table_create (_cairo_ps_font_equal); + if (!surface->fonts) { + _cairo_output_stream_destroy (surface->stream); + fclose (surface->tmpfile); + free (surface); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; + } + surface->max_font = 0; + + surface->width = width; + surface->height = height; + surface->x_dpi = PS_SURFACE_DPI_DEFAULT; + surface->y_dpi = PS_SURFACE_DPI_DEFAULT; + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + + surface->num_pages = 0; + + return _cairo_paginated_surface_create (&surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + width, height, + &cairo_ps_surface_paginated_backend); +} + +/** + * cairo_ps_surface_create: + * @filename: a filename for the PS output (must be writable) + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written to @filename. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + **/ +cairo_surface_t * +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points) +{ + cairo_status_t status; + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + status = _cairo_output_stream_get_status (stream); + if (status) { + _cairo_error (status); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +/** + * cairo_ps_surface_create_for_stream: + * @write: a #cairo_write_func_t to accept the output data + * @closure: the closure argument for @write + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written incrementally to the stream represented by @write and + * @closure. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + */ +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_status_t status; + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + status = _cairo_output_stream_get_status (stream); + if (status) { + _cairo_error (status); + return (cairo_surface_t*) &_cairo_surface_nil; + } + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +static cairo_bool_t +_cairo_surface_is_ps (cairo_surface_t *surface) +{ + return surface->backend == &cairo_ps_surface_backend; +} + +/** + * cairo_ps_surface_set_dpi: + * @surface: a postscript cairo_surface_t + * @x_dpi: horizontal dpi + * @y_dpi: vertical dpi + * + * Set the horizontal and vertical resolution for image fallbacks. + * When the ps backend needs to fall back to image overlays, it will + * use this resolution. These DPI values are not used for any other + * purpose, (in particular, they do not have any bearing on the size + * passed to cairo_ps_surface_create() nor on the CTM). + **/ +void +cairo_ps_surface_set_dpi (cairo_surface_t *surface, + double x_dpi, + double y_dpi) +{ + cairo_surface_t *target; + cairo_ps_surface_t *ps_surface; + + if (! _cairo_surface_is_paginated (surface)) { + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + target = _cairo_paginated_surface_get_target (surface); + + if (! _cairo_surface_is_ps (surface)) { + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + ps_surface = (cairo_ps_surface_t *) target; + + ps_surface->x_dpi = x_dpi; + ps_surface->y_dpi = y_dpi; + +} + +/* A word wrap stream can be used as a filter to do word wrapping on + * top of an existing output stream. The word wrapping is quite + * simple, using isspace to determine characters that separate + * words. Any word that will cause the column count exceeed the given + * max_column will have a '\n' character emitted before it. + * + * The stream is careful to maintain integrity for words that cross + * the boundary from one call to write to the next. + * + * Note: This stream does not guarantee that the output will never + * exceed max_column. In particular, if a single word is larger than + * max_column it will not be broken up. + */ +typedef struct _word_wrap_stream { + cairo_output_stream_t *output; + int max_column; + int column; + cairo_bool_t last_write_was_space; +} word_wrap_stream_t; + +static int +_count_word_up_to (const unsigned char *s, int length) +{ + int word = 0; + + while (length--) { + if (! isspace (*s++)) + word++; + else + return word; + } + + return word; +} + +static cairo_status_t +_word_wrap_stream_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + word_wrap_stream_t *stream = closure; + cairo_bool_t newline; + int word; + + while (length) { + if (isspace (*data)) { + newline = (*data == '\n' || *data == '\r'); + if (! newline && stream->column >= stream->max_column) { + _cairo_output_stream_printf (stream->output, "\n"); + stream->column = 0; + } + _cairo_output_stream_write (stream->output, data, 1); + data++; + length--; + if (newline) + stream->column = 0; + else + stream->column++; + stream->last_write_was_space = TRUE; + } else { + word = _count_word_up_to (data, length); + /* Don't wrap if this word is a continuation of a word + * from a previous call to write. */ + if (stream->column + word >= stream->max_column && + stream->last_write_was_space) + { + _cairo_output_stream_printf (stream->output, "\n"); + stream->column = 0; + } + _cairo_output_stream_write (stream->output, data, word); + data += word; + length -= word; + stream->column += word; + stream->last_write_was_space = FALSE; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_output_stream_t * +_word_wrap_stream_create (cairo_output_stream_t *output, int max_column) +{ + word_wrap_stream_t *stream; + + stream = malloc (sizeof (word_wrap_stream_t)); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; + + stream->output = output; + stream->max_column = max_column; + stream->column = 0; + stream->last_write_was_space = FALSE; + + return _cairo_output_stream_create (_word_wrap_stream_write, + NULL, stream); +} + +static cairo_status_t +_cairo_ps_surface_finish (void *abstract_surface) +{ + cairo_status_t status; + cairo_ps_surface_t *surface = abstract_surface; + cairo_output_stream_t *final_stream, *word_wrap; + + /* Save final_stream to be restored later. */ + final_stream = surface->final_stream; + + word_wrap = _word_wrap_stream_create (final_stream, 79); + surface->final_stream = word_wrap; + + _cairo_ps_surface_emit_header (surface); + + _cairo_ps_surface_emit_fonts (surface); + + _cairo_ps_surface_emit_body (surface); + + _cairo_ps_surface_emit_footer (surface); + + _cairo_output_stream_close (surface->stream); + status = _cairo_output_stream_get_status (surface->stream); + _cairo_output_stream_destroy (surface->stream); + + fclose (surface->tmpfile); + + /* Restore final stream before final cleanup. */ + _cairo_output_stream_destroy (word_wrap); + surface->final_stream = final_stream; + + _cairo_output_stream_close (surface->final_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_output_stream_get_status (surface->final_stream); + _cairo_output_stream_destroy (surface->final_stream); + + return status; +} + +static cairo_int_status_t +_cairo_ps_surface_start_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + /* Increment before print so page numbers start at 1. */ + surface->num_pages++; + _cairo_output_stream_printf (surface->stream, + "%%%%Page: %d %d\n", + surface->num_pages, + surface->num_pages); + + _cairo_output_stream_printf (surface->stream, + "gsave %f %f translate %f %f scale \n", + 0.0, surface->height, + 1.0/surface->base.device_x_scale, + -1.0/surface->base.device_y_scale); + + return _cairo_output_stream_get_status (surface->stream); +} + +static void +_cairo_ps_surface_end_page (cairo_ps_surface_t *surface) +{ + _cairo_output_stream_printf (surface->stream, + "grestore\n"); +} + +static cairo_int_status_t +_cairo_ps_surface_copy_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + _cairo_ps_surface_end_page (surface); + + _cairo_output_stream_printf (surface->stream, "copypage\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_ps_surface_show_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + _cairo_ps_surface_end_page (surface); + + _cairo_output_stream_printf (surface->stream, "showpage\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +color_is_gray (cairo_color_t *color) +{ + const double epsilon = 0.00001; + + return (fabs (color->red - color->green) < epsilon && + fabs (color->red - color->blue) < epsilon); +} + +static cairo_bool_t +surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (pattern->surface->backend->acquire_source_image != NULL) + return TRUE; + + return FALSE; +} + +static cairo_bool_t +pattern_supported (const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); + + return FALSE; +} + +static cairo_int_status_t +operation_supported (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (! pattern_supported (pattern)) + return FALSE; + + if (_cairo_operator_always_opaque (op)) + return TRUE; + + if (_cairo_operator_always_translucent (op)) + return FALSE; + + return _cairo_pattern_is_opaque (pattern); +} + +static cairo_int_status_t +_analyze_operation (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (operation_supported (surface, op, pattern)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +/* The "standard" implementation limit for PostScript string sizes is + * 65535 characters (see PostScript Language Reference, Appendix + * B). We go one short of that because we sometimes need two + * characters in a string to represent a single ASCII85 byte, (for the + * escape sequences "\\", "\(", and "\)") and we must not split these + * across two strings. So we'd be in trouble if we went right to the + * limit and one of these escape sequences just happened to land at + * the end. + */ +#define STRING_ARRAY_MAX_STRING_SIZE (65535-1) +#define STRING_ARRAY_MAX_COLUMN 72 + +typedef struct _string_array_stream { + cairo_output_stream_t *output; + int column; + int string_size; +} string_array_stream_t; + +static cairo_status_t +_string_array_stream_write (void *closure, + const unsigned char *data, + unsigned int length) +{ + string_array_stream_t *stream = closure; + unsigned char c; + const unsigned char backslash = '\\'; + + if (length == 0) + return CAIRO_STATUS_SUCCESS; + + while (length--) { + if (stream->string_size == 0) { + _cairo_output_stream_printf (stream->output, "("); + stream->column++; + } + + c = *data++; + switch (c) { + case '\\': + case '(': + case ')': + _cairo_output_stream_write (stream->output, &backslash, 1); + stream->column++; + stream->string_size++; + break; + } + _cairo_output_stream_write (stream->output, &c, 1); + stream->column++; + stream->string_size++; + + if (stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE) { + _cairo_output_stream_printf (stream->output, ")\n"); + stream->string_size = 0; + stream->column = 0; + } + if (stream->column >= STRING_ARRAY_MAX_COLUMN) { + _cairo_output_stream_printf (stream->output, "\n "); + stream->string_size += 2; + stream->column = 1; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_string_array_stream_close (void *closure) +{ + cairo_status_t status; + string_array_stream_t *stream = closure; + + _cairo_output_stream_printf (stream->output, ")\n"); + + status = _cairo_output_stream_get_status (stream->output); + + free (stream); + + return status; +} + +/* A string_array_stream wraps an existing output stream. It takes the + * data provided to it and output one or more consecutive string + * objects, each within the standard PostScript implementation limit + * of 65k characters. + * + * The strings are each separated by a space character for easy + * inclusion within an array object, (but the array delimiters are not + * added by the string_array_stream). + * + * The string array stream is also careful to wrap the output within + * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds + * necessary escaping for special characters within a string, + * (specifically '\', '(', and ')'). + */ +static cairo_output_stream_t * +_string_array_stream_create (cairo_output_stream_t *output) +{ + string_array_stream_t *stream; + + stream = malloc (sizeof (string_array_stream_t)); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; + + stream->output = output; + stream->column = 0; + stream->string_size = 0; + + return _cairo_output_stream_create (_string_array_stream_write, + _string_array_stream_close, + stream); +} + +/* PS Output - this section handles output of the parts of the meta + * surface we can render natively in PS. */ + +static cairo_status_t +emit_image (cairo_ps_surface_t *surface, + cairo_image_surface_t *image, + cairo_matrix_t *matrix, + char *name) +{ + cairo_status_t status; + unsigned char *rgb, *compressed; + unsigned long rgb_size, compressed_size; + cairo_surface_t *opaque; + cairo_image_surface_t *opaque_image; + cairo_pattern_union_t pattern; + cairo_matrix_t d2i; + int x, y, i; + cairo_output_stream_t *base85_stream, *string_array_stream; + + /* PostScript can not represent the alpha channel, so we blend the + current image over a white RGB surface to eliminate it. */ + + if (image->base.status) + return image->base.status; + + if (image->format != CAIRO_FORMAT_RGB24) { + opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + image->width, + image->height); + if (opaque->status) { + status = CAIRO_STATUS_NO_MEMORY; + goto bail0; + } + + _cairo_pattern_init_for_surface (&pattern.surface, &image->base); + + _cairo_surface_fill_rectangle (opaque, + CAIRO_OPERATOR_SOURCE, + CAIRO_COLOR_WHITE, + 0, 0, image->width, image->height); + + _cairo_surface_composite (CAIRO_OPERATOR_OVER, + &pattern.base, + NULL, + opaque, + 0, 0, + 0, 0, + 0, 0, + image->width, + image->height); + + _cairo_pattern_fini (&pattern.base); + opaque_image = (cairo_image_surface_t *) opaque; + } else { + opaque = &image->base; + opaque_image = image; + } + + rgb_size = 3 * opaque_image->width * opaque_image->height; + rgb = malloc (rgb_size); + if (rgb == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto bail1; + } + + i = 0; + for (y = 0; y < opaque_image->height; y++) { + pixman_bits_t *pixel = (pixman_bits_t *) (opaque_image->data + y * opaque_image->stride); + for (x = 0; x < opaque_image->width; x++, pixel++) { + rgb[i++] = (*pixel & 0x00ff0000) >> 16; + rgb[i++] = (*pixel & 0x0000ff00) >> 8; + rgb[i++] = (*pixel & 0x000000ff) >> 0; + } + } + + /* XXX: Should fix cairo-lzw to provide a stream-based interface + * instead. */ + compressed_size = rgb_size; + compressed = _cairo_lzw_compress (rgb, &compressed_size); + if (compressed == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto bail2; + } + + /* First emit the image data as a base85-encoded string which will + * be used as the data source for the image operator later. */ + _cairo_output_stream_printf (surface->stream, + "/%sData [\n", name); + + string_array_stream = _string_array_stream_create (surface->stream); + base85_stream = _cairo_base85_stream_create (string_array_stream); + + _cairo_output_stream_write (base85_stream, compressed, compressed_size); + + _cairo_output_stream_destroy (base85_stream); + _cairo_output_stream_destroy (string_array_stream); + + _cairo_output_stream_printf (surface->stream, + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/%sDataIndex 0 def\n", name); + + /* matrix transforms from user space to image space. We need to + * transform from device space to image space to compensate for + * postscripts coordinate system. */ + cairo_matrix_init (&d2i, 1, 0, 0, 1, 0, 0); + cairo_matrix_multiply (&d2i, &d2i, matrix); + + _cairo_output_stream_printf (surface->stream, + "/%s {\n" + " /DeviceRGB setcolorspace\n" + " <<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [ 0 1 0 1 0 1 ]\n" + " /DataSource {\n" + " %sData %sDataIndex get\n" + " /%sDataIndex %sDataIndex 1 add def\n" + " %sDataIndex %sData length 1 sub gt { /%sDataIndex 0 def } if\n" + " } /ASCII85Decode filter /LZWDecode filter\n" + " /ImageMatrix [ %f %f %f %f %f %f ]\n" + " >>\n" + " image\n" + "} def\n", + name, + opaque_image->width, + opaque_image->height, + name, name, name, name, name, name, name, + d2i.xx, d2i.yx, + d2i.xy, d2i.yy, + d2i.x0, d2i.y0); + + status = CAIRO_STATUS_SUCCESS; + + free (compressed); + bail2: + free (rgb); + bail1: + if (opaque_image != image) + cairo_surface_destroy (opaque); + bail0: + return status; +} + +static void +emit_solid_pattern (cairo_ps_surface_t *surface, + cairo_solid_pattern_t *pattern) +{ + if (color_is_gray (&pattern->color)) + _cairo_output_stream_printf (surface->stream, + "%f G\n", + pattern->color.red); + else + _cairo_output_stream_printf (surface->stream, + "%f %f %f R\n", + pattern->color.red, + pattern->color.green, + pattern->color.blue); +} + +static void +emit_surface_pattern (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_rectangle_t extents; + + if (_cairo_surface_is_meta (pattern->surface)) { + _cairo_output_stream_printf (surface->stream, "/MyPattern {\n"); + _cairo_meta_surface_replay (pattern->surface, &surface->base); + extents.width = surface->width; + extents.height = surface->height; + _cairo_output_stream_printf (surface->stream, "} bind def\n"); + } else { + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &image_extra); + _cairo_surface_get_extents (&image->base, &extents); + assert (status == CAIRO_STATUS_SUCCESS); + emit_image (surface, image, &pattern->base.matrix, "MyPattern"); + _cairo_surface_release_source_image (pattern->surface, image, + image_extra); + } + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 1\n" + " /PaintType 1\n" + " /TilingType 1\n"); + _cairo_output_stream_printf (surface->stream, + " /BBox [0 0 %d %d]\n", + extents.width, extents.height); + _cairo_output_stream_printf (surface->stream, + " /XStep %d /YStep %d\n", + extents.width, extents.height); + _cairo_output_stream_printf (surface->stream, + " /PaintProc { MyPattern } bind\n" + ">> matrix makepattern setpattern\n"); +} + +static void +emit_linear_pattern (cairo_ps_surface_t *surface, + cairo_linear_pattern_t *pattern) +{ + /* XXX: NYI */ +} + +static void +emit_radial_pattern (cairo_ps_surface_t *surface, + cairo_radial_pattern_t *pattern) +{ + /* XXX: NYI */ +} + +static void +emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern) +{ + /* FIXME: We should keep track of what pattern is currently set in + * the postscript file and only emit code if we're setting a + * different pattern. */ + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_TYPE_SURFACE: + emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_TYPE_RADIAL: + emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); + break; + } +} + static cairo_int_status_t _cairo_ps_surface_intersect_clip_path (void *abstract_surface, cairo_path_fixed_t *path, @@ -1119,6 +1296,9 @@ _cairo_ps_surface_intersect_clip_path (void *abstract_surface, cairo_status_t status; const char *ps_operator; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_intersect_clip_path\n"); @@ -1172,81 +1352,144 @@ _cairo_ps_surface_get_extents (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED static cairo_int_status_t -_cairo_ps_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - 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_ps_surface_paint (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source) { cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; - cairo_font_subset_t *subset; - int i, subset_index; - if (surface->fallback) - return CAIRO_STATUS_SUCCESS; - - if (surface->need_start_page) - _cairo_ps_surface_start_page (surface); - - /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */ - if (! _cairo_scaled_font_is_ft (scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface->fallback) - return CAIRO_STATUS_SUCCESS; - - if (pattern_operation_needs_fallback (op, pattern)) - return _cairo_ps_surface_add_fallback_area (surface, dest_x, dest_y, width, height); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _analyze_operation (surface, op, source); + /* XXX: It would be nice to be able to assert this condition + * here. But, we actually allow one 'cheat' that is used when + * painting the final image-based fallbacks. The final fallbacks + * do have alpha which we support by blending with white. This is + * possible only because there is nothing between the fallback + * images and the paper, nor is anything painted above. */ + /* + assert (pattern_operation_supported (op, source)); + */ + _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_old_show_glyphs\n"); + "%% _cairo_ps_surface_paint\n"); - emit_pattern (surface, pattern); - - /* FIXME: Need to optimize this so we only do this sequence if the - * font isn't already set. */ - - subset = _cairo_ps_surface_get_font (surface, scaled_font); - _cairo_output_stream_printf (stream, - "/f%d findfont\n" - "[ %f %f %f %f 0 0 ] makefont\n" - "setfont\n", - subset->font_id, - scaled_font->scale.xx, - scaled_font->scale.yx, - scaled_font->scale.xy, - -scaled_font->scale.yy); - - /* FIXME: Need to optimize per glyph code. Should detect when - * glyphs share the same baseline and when the spacing corresponds - * to the glyph widths. */ - - for (i = 0; i < num_glyphs; i++) { - subset_index = _cairo_font_subset_use_glyph (subset, glyphs[i].index); - _cairo_output_stream_printf (stream, - "%f %f moveto (\\%o) show\n", - glyphs[i].x, - glyphs[i].y, - subset_index); - - } + emit_pattern (surface, source); + _cairo_output_stream_printf (stream, "0 0 M\n"); + _cairo_output_stream_printf (stream, "%f 0 L\n", surface->width); + _cairo_output_stream_printf (stream, "%f %f L\n", + surface->width, surface->height); + _cairo_output_stream_printf (stream, "0 %f L\n", surface->height); + _cairo_output_stream_printf (stream, "P F\n"); return CAIRO_STATUS_SUCCESS; } -#endif + +static int +_cairo_ps_line_cap (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: + return 0; + case CAIRO_LINE_CAP_ROUND: + return 1; + case CAIRO_LINE_CAP_SQUARE: + return 2; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static int +_cairo_ps_line_join (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: + return 0; + case CAIRO_LINE_JOIN_ROUND: + return 1; + case CAIRO_LINE_JOIN_BEVEL: + return 2; + default: + ASSERT_NOT_REACHED; + return 0; + } +} static cairo_int_status_t -_cairo_ps_surface_fill (void *abstract_surface, +_cairo_ps_surface_stroke (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->stream; + cairo_int_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _analyze_operation (surface, op, source); + + assert (operation_supported (surface, op, source)); + + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_stroke\n"); + + emit_pattern (surface, source); + + _cairo_output_stream_printf (stream, + "gsave\n"); + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_ps_surface_path_move_to, + _cairo_ps_surface_path_line_to, + _cairo_ps_surface_path_curve_to, + _cairo_ps_surface_path_close_path, + stream); + + /* + * Switch to user space to set line parameters + */ + _cairo_output_stream_printf (stream, + "[%f %f %f %f 0 0] concat\n", + ctm->xx, ctm->yx, ctm->xy, ctm->yy); + /* line width */ + _cairo_output_stream_printf (stream, "%f setlinewidth\n", + style->line_width); + /* line cap */ + _cairo_output_stream_printf (stream, "%d setlinecap\n", + _cairo_ps_line_cap (style->line_cap)); + /* line join */ + _cairo_output_stream_printf (stream, "%d setlinejoin\n", + _cairo_ps_line_join (style->line_join)); + /* dashes */ + if (style->num_dashes) { + int d; + _cairo_output_stream_printf (stream, "["); + for (d = 0; d < style->num_dashes; d++) + _cairo_output_stream_printf (stream, " %f", style->dash[d]); + _cairo_output_stream_printf (stream, "] %f setdash\n", + style->dash_offset); + } + /* miter limit */ + _cairo_output_stream_printf (stream, "%f setmiterlimit\n", + style->miter_limit); + _cairo_output_stream_printf (stream, + "stroke\n"); + _cairo_output_stream_printf (stream, + "grestore\n"); + return status; +} + +static cairo_int_status_t +_cairo_ps_surface_fill (void *abstract_surface, cairo_operator_t op, cairo_pattern_t *source, cairo_path_fixed_t *path, @@ -1259,15 +1502,11 @@ _cairo_ps_surface_fill (void *abstract_surface, cairo_int_status_t status; const char *ps_operator; - if (pattern_operation_needs_fallback (op, source)) - return _cairo_ps_surface_add_fallback_area (surface, - 0, 0, - surface->width, - surface->height); - - if (surface->need_start_page) - _cairo_ps_surface_start_page (surface); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _analyze_operation (surface, op, source); + assert (operation_supported (surface, op, source)); + _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_fill\n"); @@ -1283,7 +1522,7 @@ _cairo_ps_surface_fill (void *abstract_surface, switch (fill_rule) { case CAIRO_FILL_RULE_WINDING: - ps_operator = "fill"; + ps_operator = "F"; break; case CAIRO_FILL_RULE_EVEN_ODD: ps_operator = "eofill"; @@ -1298,7 +1537,93 @@ _cairo_ps_surface_fill (void *abstract_surface, return status; } +static char +hex_digit (int i) +{ + i &= 0xf; + if (i < 10) return '0' + i; + return 'a' + (i - 10); +} + +static cairo_int_status_t +_cairo_ps_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->stream; + cairo_int_status_t status; + cairo_path_fixed_t *path; + int i; + int cur_subfont = -1, subfont; + cairo_ps_font_t *ps_font; + cairo_ps_glyph_t *ps_glyph; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _analyze_operation (surface, op, source); + + assert (operation_supported (surface, op, source)); + + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_show_glyphs\n"); + status = _cairo_ps_font_find (surface, scaled_font, &ps_font); + if (status) + goto fallback; + + if (num_glyphs) + emit_pattern (surface, source); + + for (i = 0; i < num_glyphs; i++) { + status = _cairo_ps_glyph_find (ps_font, scaled_font, + glyphs[i].index, &ps_glyph); + if (status) { + glyphs += i; + num_glyphs -= i; + goto fallback; + } + subfont = ps_glyph->output_glyph >> 8; + if (subfont != cur_subfont) { + _cairo_output_stream_printf (surface->stream, + "/CairoFont-%d-%d 1 selectfont\n", + ps_font->output_font, + subfont); + cur_subfont = subfont; + } + _cairo_output_stream_printf (surface->stream, + "%f %f M <%c%c> S\n", + glyphs[i].x, glyphs[i].y, + hex_digit (ps_glyph->output_glyph >> 4), + hex_digit (ps_glyph->output_glyph)); + } + + return CAIRO_STATUS_SUCCESS; + +fallback: + + path = _cairo_path_fixed_create (); + _cairo_scaled_font_glyph_path (scaled_font, glyphs, num_glyphs, path); + status = _cairo_ps_surface_fill (abstract_surface, op, source, + path, CAIRO_FILL_RULE_WINDING, + 0.1, scaled_font->options.antialias); + _cairo_path_fixed_destroy (path); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_ps_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_ps_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; +} + static const cairo_surface_backend_t cairo_ps_surface_backend = { + CAIRO_SURFACE_TYPE_PS, NULL, /* create_similar */ _cairo_ps_surface_finish, NULL, /* acquire_source_image */ @@ -1306,19 +1631,15 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { NULL, /* acquire_dest_image */ NULL, /* release_dest_image */ NULL, /* clone_similar */ - _cairo_ps_surface_composite, - _cairo_ps_surface_fill_rectangles, - _cairo_ps_surface_composite_trapezoids, + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ _cairo_ps_surface_copy_page, _cairo_ps_surface_show_page, NULL, /* set_clip_region */ _cairo_ps_surface_intersect_clip_path, _cairo_ps_surface_get_extents, -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED - _cairo_ps_surface_old_show_glyphs, -#else NULL, /* old_show_glyphs */ -#endif NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ @@ -1327,9 +1648,15 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { /* Here are the drawing functions */ - NULL, /* paint */ + _cairo_ps_surface_paint, /* paint */ NULL, /* mask */ - NULL, /* stroke */ + _cairo_ps_surface_stroke, _cairo_ps_surface_fill, - NULL /* show_glyphs */ + _cairo_ps_surface_show_glyphs, + NULL, /* snapshot */ +}; + +static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = { + _cairo_ps_surface_start_page, + _cairo_ps_surface_set_paginated_mode }; diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index d291c7666..8b901352b 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -44,15 +44,11 @@ typedef struct cairo_quartz_surface { CGContextRef context; - cairo_bool_t flipped; + cairo_bool_t y_grows_down; - int width; - int height; + cairo_rectangle_t extents; - cairo_image_surface_t *image; - pixman_region16_t *clip_region; - - CGImageRef cgImage; + pixman_region16_t *clip_region; } cairo_quartz_surface_t; cairo_bool_t diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index d7defc98e..e5b683ac4 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -37,27 +37,13 @@ #include "cairo-private.h" #include "cairo-quartz-private.h" -static void -ImageDataReleaseFunc(void *info, const void *data, size_t size) -{ - if (data != NULL) { - free((void *) data); - } -} - static cairo_status_t _cairo_quartz_surface_finish(void *abstract_surface) { cairo_quartz_surface_t *surface = abstract_surface; - if (surface->image) - cairo_surface_destroy(&surface->image->base); - - if (surface->cgImage) - CGImageRelease(surface->cgImage); - - if (surface->clip_region) - pixman_region_destroy (surface->clip_region); + if (surface->clip_region) + pixman_region_destroy (surface->clip_region); return CAIRO_STATUS_SUCCESS; } @@ -67,63 +53,13 @@ _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; - // 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); + if (CGBitmapContextGetBitmapInfo (surface->context) != 0) { + /* XXX: We can create an image out of the bitmap here */ + } - *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); - - 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); - if (surface->image->base.status) { - if (surface->cgImage) - CGImageRelease(surface->cgImage); - return CAIRO_STATUS_NO_MEMORY; - } - - *image_out = surface->image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t @@ -135,19 +71,79 @@ _cairo_quartz_surface_acquire_dest_image(void *abstract_surface, void **image_extra) { cairo_quartz_surface_t *surface = abstract_surface; + cairo_surface_t *image_surface; + unsigned char *data; + int x1, y1, x2, y2; - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->image->width; - image_rect->height = surface->image->height; + x1 = surface->extents.x; + x2 = surface->extents.x + surface->extents.width; + y1 = surface->extents.y; + y2 = surface->extents.y + surface->extents.height; - *image_out = surface->image; - if (image_extra) + 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; + } + + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + + data = calloc (image_rect->width * image_rect->height * 4, 1); + image_surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + image_rect->width, + image_rect->height, + image_rect->width * 4); + + *image_out = (cairo_image_surface_t *)image_surface; + *image_extra = data; return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; } +static CGImageRef +create_image_from_surface (cairo_image_surface_t *image_surface, void *data) +{ + CGImageRef image; + CGColorSpaceRef color_space; + CGDataProviderRef data_provider; + int width, height; + + width = cairo_image_surface_get_width ((cairo_surface_t *)image_surface); + height = cairo_image_surface_get_height ((cairo_surface_t *)image_surface); + + color_space = CGColorSpaceCreateDeviceRGB(); + data_provider = CGDataProviderCreateWithData (NULL, data, + width * height * 4, NULL); + image = CGImageCreate (width, height, + 8, 32, + width * 4, + color_space, + kCGImageAlphaPremultipliedFirst, + data_provider, + NULL, + FALSE, kCGRenderingIntentDefault); + + CGColorSpaceRelease (color_space); + CGDataProviderRelease (data_provider); + + return image; +} static void _cairo_quartz_surface_release_dest_image(void *abstract_surface, @@ -158,25 +154,28 @@ _cairo_quartz_surface_release_dest_image(void *abstract_surface, void *image_extra) { cairo_quartz_surface_t *surface = abstract_surface; + CGImageRef image_ref; + CGRect rect; + + image_ref = create_image_from_surface (image, image_extra); - if (surface->image == image) { - CGRect rect; + rect = CGRectMake (image_rect->x, image_rect->y, image_rect->width, image_rect->height); - rect = CGRectMake(0, 0, surface->width, surface->height); - - if (surface->flipped) { - CGContextSaveGState (surface->context); - CGContextTranslateCTM (surface->context, 0, surface->height); - CGContextScaleCTM (surface->context, 1, -1); - } - - CGContextDrawImage(surface->context, rect, surface->cgImage); - - if (surface->flipped) - CGContextRestoreGState (surface->context); - - memset(surface->image->data, 0, surface->width * surface->height * 4); + if (surface->y_grows_down) { + CGContextSaveGState (surface->context); + CGContextTranslateCTM (surface->context, 0, image_rect->height + 2 * image_rect->y); + CGContextScaleCTM (surface->context, 1, -1); } + + CGContextDrawImage(surface->context, rect, image_ref); + CFRelease (image_ref); + + if (surface->y_grows_down) { + CGContextRestoreGState (surface->context); + } + + cairo_surface_destroy ((cairo_surface_t *)image); + free (image_extra); } static cairo_int_status_t @@ -184,21 +183,17 @@ _cairo_quartz_surface_set_clip_region(void *abstract_surface, pixman_region16_t * region) { cairo_quartz_surface_t *surface = abstract_surface; - unsigned int serial; - serial = _cairo_surface_allocate_clip_serial (&surface->image->base); + if (surface->clip_region) + pixman_region_destroy (surface->clip_region); + + if (region) { + surface->clip_region = pixman_region_create (); + pixman_region_copy (surface->clip_region, region); + } else + surface->clip_region = NULL; - if (surface->clip_region) - pixman_region_destroy (surface->clip_region); - - if (region) { - surface->clip_region = pixman_region_create (); - pixman_region_copy (surface->clip_region, region); - } else - surface->clip_region = NULL; - - return _cairo_surface_set_clip_region(&surface->image->base, - region, serial); + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -207,15 +202,13 @@ _cairo_quartz_surface_get_extents (void *abstract_surface, { cairo_quartz_surface_t *surface = abstract_surface; - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; + *rectangle = surface->extents; return CAIRO_STATUS_SUCCESS; } static const struct _cairo_surface_backend cairo_quartz_surface_backend = { + CAIRO_SURFACE_TYPE_QUARTZ, NULL, /* create_similar */ _cairo_quartz_surface_finish, _cairo_quartz_surface_acquire_source_image, @@ -236,10 +229,12 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = { cairo_surface_t *cairo_quartz_surface_create(CGContextRef context, - cairo_bool_t flipped, - int width, int height) + int width, + int height, + cairo_bool_t y_grows_down) { cairo_quartz_surface_t *surface; + CGRect clip_box; surface = malloc(sizeof(cairo_quartz_surface_t)); if (surface == NULL) { @@ -250,16 +245,14 @@ cairo_surface_t *cairo_quartz_surface_create(CGContextRef context, _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend); surface->context = context; - surface->width = width; - surface->height = height; - surface->image = NULL; - surface->cgImage = NULL; - surface->clip_region = NULL; - surface->flipped = flipped; + surface->clip_region = NULL; + surface->y_grows_down = y_grows_down; - // 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); + clip_box = CGContextGetClipBoundingBox (context); + surface->extents.x = clip_box.origin.x; + surface->extents.y = clip_box.origin.y; + surface->extents.width = clip_box.size.width; + surface->extents.height = clip_box.size.height; return (cairo_surface_t *) surface; } diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h index 9ef537ed3..b3072df76 100644 --- a/src/cairo-quartz.h +++ b/src/cairo-quartz.h @@ -47,9 +47,9 @@ CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_quartz_surface_create (CGContextRef context, - cairo_bool_t flipped, int width, - int height); + int height, + cairo_bool_t y_grows_down); CAIRO_END_DECLS diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index 8ebbf455f..91e62578f 100644 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -39,10 +39,10 @@ #include "cairoint.h" static cairo_bool_t -_cairo_scaled_glyph_keys_equal (void *abstract_key_a, void *abstract_key_b) +_cairo_scaled_glyph_keys_equal (const void *abstract_key_a, const void *abstract_key_b) { - cairo_scaled_glyph_t *key_a = abstract_key_a; - cairo_scaled_glyph_t *key_b = abstract_key_b; + const cairo_scaled_glyph_t *key_a = abstract_key_a; + const cairo_scaled_glyph_t *key_b = abstract_key_b; return (_cairo_scaled_glyph_index (key_a) == _cairo_scaled_glyph_index (key_b)); @@ -118,6 +118,18 @@ _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, _cairo_error (status); } +/** + * cairo_scaled_font_get_type: + * @scaled_font: a #cairo_scaled_font_t + * + * Return value: The type of @scaled_font. See #cairo_font_type_t. + **/ +cairo_font_type_t +cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend->type; +} + /** * cairo_scaled_font_status: * @scaled_font: a #cairo_scaled_font_t @@ -168,7 +180,7 @@ static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL; CAIRO_MUTEX_DECLARE (cairo_scaled_font_map_mutex); static int -_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b); +_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b); static cairo_scaled_font_map_t * _cairo_scaled_font_map_lock (void) @@ -286,10 +298,10 @@ _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, } static cairo_bool_t -_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b) +_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b) { - cairo_scaled_font_t *key_a = abstract_key_a; - cairo_scaled_font_t *key_b = abstract_key_b; + const cairo_scaled_font_t *key_a = abstract_key_a; + const cairo_scaled_font_t *key_b = abstract_key_b; return (key_a->font_face == key_b->font_face && memcmp ((unsigned char *)(&key_a->font_matrix.xx), @@ -1008,7 +1020,7 @@ _scaled_glyph_path_close_path (void *abstract_closure) cairo_status_t _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, + const cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path) { diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c index 8f18ca33b..a5c723f15 100644 --- a/src/cairo-surface-fallback.c +++ b/src/cairo-surface-fallback.c @@ -451,6 +451,10 @@ _composite_trap_region (cairo_clip_t *clip, extents->x, extents->y, extents->width, extents->height); + /* Restore the original clip if we modified it temporarily. */ + if (num_rects >1) + _cairo_surface_set_clip (dst, clip); + if (clip_surface) _cairo_pattern_fini (&mask.base); @@ -549,6 +553,9 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, return status; clear_region = _cairo_region_create_from_rectangle (&extents); + if (clear_region == NULL) + return CAIRO_STATUS_NO_MEMORY; + status = _cairo_clip_intersect_to_region (clip, clear_region); if (status) return status; @@ -576,7 +583,7 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; - if ((src->type == CAIRO_PATTERN_SOLID || op == CAIRO_OPERATOR_CLEAR) && + if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && !clip_surface) { const cairo_color_t *color; diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 29820173e..9a15db4f6 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -43,6 +43,7 @@ const cairo_surface_t _cairo_surface_nil = { &cairo_image_surface_backend, /* backend */ + CAIRO_SURFACE_TYPE_IMAGE, -1, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ FALSE, /* finished */ @@ -59,6 +60,7 @@ const cairo_surface_t _cairo_surface_nil = { const cairo_surface_t _cairo_surface_nil_file_not_found = { &cairo_image_surface_backend, /* backend */ + CAIRO_SURFACE_TYPE_IMAGE, -1, /* ref_count */ CAIRO_STATUS_FILE_NOT_FOUND, /* status */ FALSE, /* finished */ @@ -75,6 +77,7 @@ const cairo_surface_t _cairo_surface_nil_file_not_found = { const cairo_surface_t _cairo_surface_nil_read_error = { &cairo_image_surface_backend, /* backend */ + CAIRO_SURFACE_TYPE_IMAGE, -1, /* ref_count */ CAIRO_STATUS_READ_ERROR, /* status */ FALSE, /* finished */ @@ -118,6 +121,22 @@ _cairo_surface_set_error (cairo_surface_t *surface, _cairo_error (status); } +/** + * cairo_surface_get_type: + * @surface: a #cairo_surface_t + * + * Return value: The type of @surface. See #cairo_surface_type_t. + **/ +cairo_surface_type_t +cairo_surface_get_type (cairo_surface_t *surface) +{ + /* We don't use surface->backend->type here so that some of the + * special "wrapper" surfaces such as cairo_paginated_surface_t + * can override surface->type with the type of the "child" + * surface. */ + return surface->type; +} + /** * cairo_surface_status: * @surface: a #cairo_surface_t @@ -141,6 +160,8 @@ _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend) { surface->backend = backend; + + surface->type = backend->type; surface->ref_count = 1; surface->status = CAIRO_STATUS_SUCCESS; @@ -186,7 +207,8 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, * * Create a new surface that is as compatible as possible with an * existing surface. The new surface will use the same backend as - * @other unless that is not possible for some reason. + * @other unless that is not possible for some reason. The type of the + * returned surface may be examined with cairo_surface_get_type(). * * Return value: a pointer to the newly allocated surface. The caller * owns the surface and should call cairo_surface_destroy when done @@ -502,6 +524,10 @@ cairo_surface_mark_dirty (cairo_surface_t *surface) * Like cairo_surface_mark_dirty(), but drawing has been done only to * the specified rectangle, so that cairo can retain cached contents * for other parts of the surface. + * + * Any cached clip set on the surface will be reset by this function, + * to make sure that future cairo calls have the clip set that they + * expect. */ void cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, @@ -520,6 +546,13 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, return; } + /* Always reset the clip here, to avoid having external calls to + * clip manipulation functions of the underlying device clip result + * in a desync between the cairo clip and the backend clip, due to + * the clip caching. + */ + surface->current_clip_serial = -1; + if (surface->backend->mark_dirty_rectangle) { cairo_status_t status; @@ -1329,6 +1362,12 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) if (!surface) return CAIRO_STATUS_NULL_POINTER; + if (surface->status) + return surface->status; + + if (surface->finished) + return CAIRO_STATUS_SURFACE_FINISHED; + if (clip) { serial = clip->serial; if (serial == 0) @@ -1336,7 +1375,7 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) } surface->clip = clip; - + if (serial == _cairo_surface_get_current_clip_serial (surface)) return CAIRO_STATUS_SUCCESS; @@ -1654,3 +1693,39 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle, dst_x, dst_y, width, height); } + +static cairo_bool_t +_format_is_opaque (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + return FALSE; + case CAIRO_FORMAT_RGB24: + return TRUE; + case CAIRO_FORMAT_A8: + return FALSE; + case CAIRO_FORMAT_A1: + return TRUE; + } + return FALSE; +} + +/* XXX: This function is funny in a couple of ways. First it seems to + * be computing something like "not translucent" rather than "opaque" + * since it returns TRUE for an A1 image surface. Second, it just + * gives up on anything other than an image surface. + * + * I imagine something that might be more useful here (or in addition) + * would be cairo_surface_get_content. + */ +cairo_bool_t +_cairo_surface_is_opaque (const cairo_surface_t *surface) +{ + if (_cairo_surface_is_image (surface)) { + const cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + return _format_is_opaque (image_surface->format); + } + + return FALSE; +} diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 929700569..90a053a6f 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -75,11 +75,15 @@ struct cairo_svg_document { unsigned int filter_id; unsigned int clip_id; unsigned int mask_id; + + cairo_bool_t alpha_filter; }; struct cairo_svg_surface { cairo_surface_t base; + cairo_content_t content; + unsigned int id; double width; @@ -88,8 +92,12 @@ struct cairo_svg_surface { cairo_svg_document_t *document; xmlNodePtr xml_node; + xmlNodePtr xml_root_node; unsigned int clip_level; + + cairo_bool_t modified; + unsigned int previous_id; }; static cairo_svg_document_t * @@ -108,8 +116,9 @@ _cairo_svg_document_reference (cairo_svg_document_t *document); static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, - double width, - double height); + cairo_content_t content, + double width, + double height); static const cairo_surface_backend_t cairo_svg_surface_backend; @@ -125,7 +134,8 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, if (document == NULL) return NULL; - surface = _cairo_svg_surface_create_for_document (document, width, height); + surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, + width, height); document->owner = surface; _cairo_svg_document_destroy (document); @@ -133,35 +143,90 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, return surface; } +/** + * cairo_svg_surface_create_for_stream: + * @write: a #cairo_write_func_t to accept the output data + * @closure: the closure argument for @write + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * incrementally to the stream represented by @write and @closure. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + */ cairo_surface_t * cairo_svg_surface_create_for_stream (cairo_write_func_t write, void *closure, double width, double height) { + cairo_status_t status; cairo_output_stream_t *stream; - stream = _cairo_output_stream_create (write, closure); - if (stream == NULL) - return NULL; + stream = _cairo_output_stream_create (write, NULL, closure); + status = _cairo_output_stream_get_status (stream); + if (status) { + _cairo_error (status); + return (cairo_surface_t *) &_cairo_surface_nil; + } return _cairo_svg_surface_create_for_stream_internal (stream, width, height); } +/** + * cairo_svg_surface_create: + * @filename: a filename for the SVG output (must be writable) + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * to @filename. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + **/ cairo_surface_t * cairo_svg_surface_create (const char *filename, double width, double height) { + cairo_status_t status; cairo_output_stream_t *stream; - stream = _cairo_output_stream_create_for_file (filename); - if (stream == NULL) - return NULL; + stream = _cairo_output_stream_create_for_filename (filename); + status = _cairo_output_stream_get_status (stream); + if (status) { + _cairo_error (status); + return (cairo_surface_t *) &_cairo_surface_nil; + } return _cairo_svg_surface_create_for_stream_internal (stream, width, height); } +/** + * cairo_svg_surface_set_dpi: + * @surface: a svg cairo_surface_t + * @x_dpi: horizontal dpi + * @y_dpi: vertical dpi + * + * Set the horizontal and vertical resolution for image fallbacks. + * When the svg backend needs to fall back to image overlays, it will + * use this resolution. These DPI values are not used for any other + * purpose, (in particular, they do not have any bearing on the size + * passed to cairo_svg_surface_create() nor on the CTM). + **/ void cairo_svg_surface_set_dpi (cairo_surface_t *surface, double x_dpi, @@ -175,11 +240,12 @@ cairo_svg_surface_set_dpi (cairo_surface_t *surface, static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, - double width, - double height) + cairo_content_t content, + double width, + double height) { cairo_svg_surface_t *surface; - xmlNodePtr clip, clip_rect; + xmlNodePtr clip, rect; int clip_id; char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; @@ -203,22 +269,33 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, clip = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("clipPath"), NULL); snprintf (buffer, sizeof buffer, "clip%d", clip_id); xmlSetProp (clip, CC2XML ("id"), C2XML (buffer)); - clip_rect = xmlNewChild (clip, NULL, CC2XML ("rect"), NULL); + rect = xmlNewChild (clip, NULL, CC2XML ("rect"), NULL); _cairo_dtostr (buffer, sizeof buffer, width); - xmlSetProp (clip_rect, CC2XML ("width"), C2XML (buffer)); + xmlSetProp (rect, CC2XML ("width"), C2XML (buffer)); _cairo_dtostr (buffer, sizeof buffer, height); - xmlSetProp (clip_rect, CC2XML ("height"), C2XML (buffer)); + xmlSetProp (rect, CC2XML ("height"), C2XML (buffer)); - surface->xml_node = xmlNewChild (surface->id == 0 ? - document->xml_node_main : - document->xml_node_defs, - NULL, CC2XML ("g"), NULL); + surface->xml_node = xmlNewNode (NULL, CC2XML ("g")); + surface->xml_root_node = surface->xml_node; snprintf (buffer, sizeof buffer, "surface%d", surface->id); xmlSetProp (surface->xml_node, CC2XML ("id"), C2XML (buffer)); snprintf (buffer, sizeof buffer, "url(#clip%d)", clip_id); xmlSetProp (surface->xml_node, CC2XML ("clip-path"), C2XML (buffer)); - + + if (content == CAIRO_CONTENT_COLOR) { + rect = xmlNewChild (surface->xml_node, NULL, CC2XML ("rect"), NULL); + _cairo_dtostr (buffer, sizeof buffer, width); + xmlSetProp (rect, CC2XML ("width"), C2XML (buffer)); + _cairo_dtostr (buffer, sizeof buffer, height); + xmlSetProp (rect, CC2XML ("height"), C2XML (buffer)); + xmlSetProp (rect, CC2XML ("style"), CC2XML ("opacity:1; stroke:none; fill:rgb(0,0,0);")); + } + + surface->modified = TRUE; + surface->previous_id = surface->id; + surface->content = content; + return &surface->base; } @@ -231,7 +308,7 @@ _cairo_svg_surface_create_similar (void *abstract_src, cairo_svg_surface_t *template = abstract_src; return _cairo_svg_surface_create_for_document (template->document, - width, height); + content, width, height); } static cairo_status_t @@ -240,17 +317,45 @@ _cairo_svg_surface_finish (void *abstract_surface) cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; + - if (document->owner == &surface->base) + if (document->owner == &surface->base) { + xmlAddChild (document->xml_node_main, xmlCopyNode (surface->xml_root_node, 1)); status = _cairo_svg_document_finish (document); - else + } else status = CAIRO_STATUS_SUCCESS; _cairo_svg_document_destroy (document); + xmlFreeNode (surface->xml_root_node); + surface->xml_node = NULL; + return status; } +static void +emit_alpha_filter (cairo_svg_document_t *document) +{ + if (!document->alpha_filter) { + xmlNodePtr node; + xmlNodePtr child; + + node = xmlNewChild (document->xml_node_defs, NULL, + CC2XML ("filter"), NULL); + xmlSetProp (node, CC2XML ("id"), CC2XML ("alpha")); + xmlSetProp (node, CC2XML ("filterUnits"), CC2XML ("objectBoundingBox")); + xmlSetProp (node, CC2XML ("x"), CC2XML ("0%")); + xmlSetProp (node, CC2XML ("y"), CC2XML ("0%")); + xmlSetProp (node, CC2XML ("width"), CC2XML ("100%")); + xmlSetProp (node, CC2XML ("height"), CC2XML ("100%")); + child = xmlNewChild (node, NULL, CC2XML ("feColorMatrix"), NULL); + xmlSetProp (child, CC2XML("type"), CC2XML ("matrix")); + xmlSetProp (child, CC2XML("in"), CC2XML ("SourceGraphic")); + xmlSetProp (child, CC2XML("values"), CC2XML ("0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0")); + document->alpha_filter = TRUE; + } +} + static void emit_transform (xmlNodePtr node, char const *attribute_str, @@ -263,22 +368,22 @@ emit_transform (xmlNodePtr node, xmlBufferCat (matrix_buffer, CC2XML ("matrix(")); _cairo_dtostr (buffer, sizeof buffer, matrix->xx); xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, ","); + xmlBufferCat (matrix_buffer, CC2XML (",")); _cairo_dtostr (buffer, sizeof buffer, matrix->yx); xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, ","); + xmlBufferCat (matrix_buffer, CC2XML (",")); _cairo_dtostr (buffer, sizeof buffer, matrix->xy); xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, ","); + xmlBufferCat (matrix_buffer, CC2XML (",")); _cairo_dtostr (buffer, sizeof buffer, matrix->yy); xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, ","); + xmlBufferCat (matrix_buffer, CC2XML (",")); _cairo_dtostr (buffer, sizeof buffer, matrix->x0); xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, ","); + xmlBufferCat (matrix_buffer, CC2XML(",")); _cairo_dtostr (buffer, sizeof buffer, matrix->y0); xmlBufferCat (matrix_buffer, C2XML (buffer)); - xmlBufferCat (matrix_buffer, ")"); + xmlBufferCat (matrix_buffer, CC2XML (")")); xmlSetProp (node, CC2XML (attribute_str), C2XML (xmlBufferContent (matrix_buffer))); xmlBufferFree (matrix_buffer); } @@ -288,11 +393,10 @@ typedef struct { unsigned int in_mem; unsigned char src[3]; unsigned char dst[5]; - unsigned int count; unsigned int trailing; } base64_write_closure_t; -static unsigned char const *base64_table = +static char const *base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static cairo_status_t @@ -322,11 +426,6 @@ base64_write_func (void *closure, data++; length--; } - info->count++; - if (info->count >= 18) { - info->count = 0; - xmlBufferCat (info->buffer, "\r\n"); - } dst[0] = base64_table[src[0] >> 2]; dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; @@ -366,7 +465,6 @@ _cairo_surface_base64_encode (cairo_surface_t *surface, info.buffer = xmlBufferCreate(); info.in_mem = 0; - info.count = 0; info.trailing = 0; memset (info.dst, '\x0', 5); *buffer = info.buffer; @@ -450,16 +548,22 @@ emit_composite_svg_pattern (xmlNodePtr node, cairo_bool_t is_pattern) { cairo_svg_surface_t *surface = (cairo_svg_surface_t *) pattern->surface; + cairo_svg_document_t *document = surface->document; cairo_matrix_t p2u; xmlNodePtr child; char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - /* FIXME: self copy is not supported yet */ - if (surface->id == 0) - return NULL; + if (surface->modified) { + if (surface->content == CAIRO_CONTENT_ALPHA) + emit_alpha_filter (document); + child = xmlAddChild (document->xml_node_defs, xmlCopyNode (surface->xml_root_node, 1)); + if (surface->content == CAIRO_CONTENT_ALPHA) + xmlSetProp (child, CC2XML ("filter"), CC2XML("url(#alpha)")); + } child = xmlNewChild (node, NULL, CC2XML("use"), NULL); - snprintf (buffer, sizeof buffer, "#surface%d", surface->id); + snprintf (buffer, sizeof buffer, "#surface%d", + surface->modified ? surface->id : surface->previous_id); xmlSetProp (child, CC2XML ("xlink:href"), C2XML (buffer)); if (!is_pattern) { @@ -473,6 +577,14 @@ emit_composite_svg_pattern (xmlNodePtr node, if (height != NULL) *height = surface->height; + if (surface->modified) { + surface->modified = FALSE; + surface->previous_id = surface->id; + surface->id = document->surface_id++; + snprintf (buffer, sizeof buffer, "surface%d", surface->id); + xmlSetProp (surface->xml_root_node, CC2XML ("id"), C2XML (buffer)); + } + return child; } @@ -762,19 +874,19 @@ emit_pattern (cairo_svg_surface_t *surface, cairo_pattern_t *pattern, xmlBufferPtr style, int is_stroke) { switch (pattern->type) { - case CAIRO_PATTERN_SOLID: + case CAIRO_PATTERN_TYPE_SOLID: emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, style, is_stroke); break; - case CAIRO_PATTERN_SURFACE: + case CAIRO_PATTERN_TYPE_SURFACE: emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, style, is_stroke); break; - case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_TYPE_LINEAR: emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, style, is_stroke); break; - case CAIRO_PATTERN_RADIAL: + case CAIRO_PATTERN_TYPE_RADIAL: emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, style, is_stroke); break; } @@ -887,9 +999,9 @@ _cairo_svg_surface_fill (void *abstract_surface, style = xmlBufferCreate (); emit_pattern (surface, source, style, 0); - xmlBufferCat (style, " stroke: none;"); - xmlBufferCat (style, " fill-rule: "); - xmlBufferCat (style, fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd;" : "nonzero;"); + xmlBufferCat (style, CC2XML (" stroke: none;")); + xmlBufferCat (style, CC2XML (" fill-rule: ")); + xmlBufferCat (style, fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? CC2XML("evenodd;") : CC2XML ("nonzero;")); status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, @@ -907,6 +1019,7 @@ _cairo_svg_surface_fill (void *abstract_surface, xmlBufferFree (info.path); xmlBufferFree (style); + surface->modified = TRUE; return status; } @@ -939,14 +1052,15 @@ emit_paint (xmlNodePtr node, xmlBufferPtr style; char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - if (source->type == CAIRO_PATTERN_SURFACE) + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) return emit_composite_pattern (node, (cairo_surface_pattern_t *) source, NULL, NULL, FALSE); style = xmlBufferCreate (); emit_pattern (surface, source, style, 0); - xmlBufferCat (style, " stroke: none;"); + xmlBufferCat (style, CC2XML (" stroke: none;")); child = xmlNewChild (node, NULL, CC2XML ("rect"), NULL); xmlSetProp (child, CC2XML ("x"), CC2XML ("0")); @@ -970,7 +1084,40 @@ _cairo_svg_surface_paint (void *abstract_surface, { cairo_svg_surface_t *surface = abstract_surface; + /* Emulation of clear and source operators, when no clipping region + * is defined. We just delete existing content of surface root node, + * and exit early if operator is clear. */ + if (surface->clip_level == 0 && + (op == CAIRO_OPERATOR_CLEAR || + op == CAIRO_OPERATOR_SOURCE)) { + xmlNodePtr child = surface->xml_root_node->children; + + while (child != NULL) { + xmlUnlinkNode (child); + xmlFreeNode (child); + child = surface->xml_root_node->children; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->content == CAIRO_CONTENT_COLOR) { + xmlNodePtr rect; + char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; + + rect = xmlNewChild (surface->xml_node, NULL, CC2XML ("rect"), NULL); + _cairo_dtostr (buffer, sizeof buffer, surface->width); + xmlSetProp (rect, CC2XML ("width"), C2XML (buffer)); + _cairo_dtostr (buffer, sizeof buffer, surface->height); + xmlSetProp (rect, CC2XML ("height"), C2XML (buffer)); + xmlSetProp (rect, CC2XML ("style"), CC2XML ("opacity:1; stroke:none; fill:rgb(0,0,0);")); + } + surface->modified = TRUE; + return CAIRO_STATUS_SUCCESS; + } + } + emit_paint (surface->xml_node, surface, op, source); + + surface->modified = TRUE; return CAIRO_STATUS_SUCCESS; } @@ -985,10 +1132,16 @@ _cairo_svg_surface_mask (void *abstract_surface, xmlNodePtr child, mask_node; char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; - mask_node = xmlNewChild (document->xml_node_defs, NULL, CC2XML ("mask"), NULL); + emit_alpha_filter (document); + + mask_node = xmlNewNode (NULL, CC2XML ("mask")); snprintf (buffer, sizeof buffer, "mask%d", document->mask_id); xmlSetProp (mask_node, CC2XML ("id"), C2XML (buffer)); - emit_paint (mask_node, surface, op, mask); + child = xmlNewChild (mask_node, NULL, CC2XML ("g"), NULL); + xmlSetProp (child, CC2XML ("filter"), CC2XML ("url(#alpha)")); + emit_paint (child, surface, op, mask); + + xmlAddChild (document->xml_node_defs, mask_node); child = emit_paint (surface->xml_node, surface, op, source); @@ -999,6 +1152,7 @@ _cairo_svg_surface_mask (void *abstract_surface, document->mask_id++; + surface->modified = TRUE; return CAIRO_STATUS_SUCCESS; } @@ -1064,21 +1218,21 @@ _cairo_svg_surface_stroke (void *abstract_dst, xmlBufferCat (style, CC2XML (" stroke-dasharray: ")); for (i = 0; i < stroke_style->num_dashes; i++) { if (i != 0) - xmlBufferCat (style, ","); + xmlBufferCat (style, CC2XML (",")); /* FIXME: Is is really what we want ? */ rx = ry = stroke_style->dash[i]; cairo_matrix_transform_distance (ctm, &rx, &ry); _cairo_dtostr (buffer, sizeof buffer, sqrt ((rx * rx + ry * ry) / 2.0)); xmlBufferCat (style, C2XML (buffer)); } - xmlBufferCat (style, ";"); + xmlBufferCat (style, CC2XML (";")); if (stroke_style->dash_offset != 0.0) { xmlBufferCat (style, CC2XML (" stroke-dashoffset: ")); rx = ry = stroke_style->dash_offset; cairo_matrix_transform_distance (ctm, &rx, &ry); _cairo_dtostr (buffer, sizeof buffer, sqrt ((rx * rx + ry * ry) / 2.0)); xmlBufferCat (style, C2XML (buffer)); - xmlBufferCat (style, ";"); + xmlBufferCat (style, CC2XML (";")); } } @@ -1103,6 +1257,7 @@ _cairo_svg_surface_stroke (void *abstract_dst, xmlBufferFree (info.path); xmlBufferFree (style); + surface->modified = TRUE; return status; } @@ -1114,6 +1269,7 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, int num_glyphs, cairo_scaled_font_t *scaled_font) { + cairo_svg_surface_t *surface = abstract_surface; cairo_path_fixed_t path; cairo_status_t status; @@ -1137,6 +1293,7 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, _cairo_path_fixed_fini (&path); + surface->modified = TRUE; return status; } @@ -1155,10 +1312,8 @@ _cairo_svg_surface_intersect_clip_path (void *dst, char buffer[CAIRO_SVG_DTOSTR_BUFFER_LEN]; if (path == NULL) { - while (surface->clip_level > 0) { - surface->xml_node = surface->xml_node->parent; - surface->clip_level--; - } + surface->xml_node = surface->xml_root_node; + surface->clip_level = 0; return CAIRO_STATUS_SUCCESS; } @@ -1208,6 +1363,7 @@ _cairo_svg_surface_get_font_options (void *abstract_surface, static const cairo_surface_backend_t cairo_svg_surface_backend = { + CAIRO_SURFACE_TYPE_SVG, _cairo_svg_surface_create_similar, _cairo_svg_surface_finish, NULL, /* acquire_source_image */ @@ -1284,6 +1440,8 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, xmlSetProp (node, CC2XML ("xmlns:xlink"), CC2XML ("http://www.w3.org/1999/xlink")); xmlSetProp (node, CC2XML ("version"), CC2XML ("1.2")); + document->alpha_filter = FALSE; + return document; } @@ -1305,21 +1463,39 @@ _cairo_svg_document_destroy (cairo_svg_document_t *document) free (document); } +static int +_cairo_svg_document_write (cairo_output_stream_t *output_stream, + const char * buffer, + int len) +{ + cairo_status_t status; + + _cairo_output_stream_write (output_stream, buffer, len); + status = _cairo_output_stream_get_status (output_stream); + if (status) { + _cairo_error (status); + return -1; + } + + return len; +} + static cairo_status_t _cairo_svg_document_finish (cairo_svg_document_t *document) { cairo_status_t status; cairo_output_stream_t *output = document->output_stream; - xmlChar *xml_buffer; - int xml_buffer_size; + xmlOutputBufferPtr xml_output_buffer; if (document->finished) return CAIRO_STATUS_SUCCESS; - /* FIXME: Dumping xml tree in memory is silly. */ - xmlDocDumpFormatMemoryEnc (document->xml_doc, &xml_buffer, &xml_buffer_size, "UTF-8", 1); - _cairo_output_stream_write (document->output_stream, xml_buffer, xml_buffer_size); - xmlFree(xml_buffer); + xml_output_buffer = xmlOutputBufferCreateIO ((xmlOutputWriteCallback) _cairo_svg_document_write, + (xmlOutputCloseCallback) NULL, + (void *) document->output_stream, + NULL); + xmlSaveFormatFileTo (xml_output_buffer, document->xml_doc, "UTF-8", 1); + xmlFreeDoc (document->xml_doc); status = _cairo_output_stream_get_status (output); diff --git a/src/cairo-wideint.c b/src/cairo-wideint.c index 9e4914e61..89df00b37 100644 --- a/src/cairo-wideint.c +++ b/src/cairo-wideint.c @@ -1,5 +1,4 @@ -/* - * $Id: cairo-wideint.c,v 1.6 2005-07-30 19:57:54 keithp Exp $ +/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Keith Packard * diff --git a/src/cairo-wideint.h b/src/cairo-wideint.h index b008b5d5a..795cde73d 100644 --- a/src/cairo-wideint.h +++ b/src/cairo-wideint.h @@ -1,5 +1,4 @@ -/* - * $Id: cairo-wideint.h,v 1.12 2005-08-05 14:48:19 cworth Exp $ +/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Keith Packard * diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c index 3343abfd4..e7c671390 100644 --- a/src/cairo-win32-font.c +++ b/src/cairo-win32-font.c @@ -1159,7 +1159,7 @@ _cairo_win32_scaled_font_show_glyphs (void *abstract_font, cairo_surface_pattern_t mask; RECT r; - tmp_surface = (cairo_win32_surface_t *)_cairo_win32_surface_create_dib (CAIRO_FORMAT_ARGB32, width, height); + tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); if (tmp_surface->base.status) return CAIRO_STATUS_NO_MEMORY; @@ -1355,6 +1355,7 @@ CLEANUP_FONT: } const cairo_scaled_font_backend_t cairo_win32_scaled_font_backend = { + CAIRO_FONT_TYPE_WIN32, _cairo_win32_scaled_font_create_toy, _cairo_win32_scaled_font_fini, _cairo_win32_scaled_font_glyph_init, @@ -1398,6 +1399,7 @@ _cairo_win32_font_face_scaled_font_create (void *abstract_face, } static const cairo_font_face_backend_t _cairo_win32_font_face_backend = { + CAIRO_FONT_TYPE_WIN32, _cairo_win32_font_face_destroy, _cairo_win32_font_face_scaled_font_create }; diff --git a/src/cairo-win32-private.h b/src/cairo-win32-private.h index a229147c7..fd6642413 100644 --- a/src/cairo-win32-private.h +++ b/src/cairo-win32-private.h @@ -62,19 +62,14 @@ typedef struct _cairo_win32_surface { cairo_rectangle_t clip_rect; - int set_clip; HRGN saved_clip; + cairo_rectangle_t extents; } cairo_win32_surface_t; cairo_status_t _cairo_win32_print_gdi_error (const char *context); -cairo_surface_t * -_cairo_win32_surface_create_dib (cairo_format_t format, - int width, - int height); - cairo_bool_t _cairo_surface_is_win32 (cairo_surface_t *surface); diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c index b1811a134..310561a82 100644 --- a/src/cairo-win32-surface.c +++ b/src/cairo-win32-surface.c @@ -31,6 +31,8 @@ * * Contributor(s): * Owen Taylor + * Stuart Parmenter + * Vladimir Vukicevic */ #include @@ -274,9 +276,14 @@ _cairo_win32_surface_create_for_dc (HDC original_dc, surface->clip_rect.width = width; surface->clip_rect.height = height; - surface->set_clip = 0; - surface->saved_clip = NULL; - + surface->saved_clip = CreateRectRgn (0, 0, 0, 0); + if (GetClipRgn (surface->dc, surface->saved_clip) == 0) { + DeleteObject(surface->saved_clip); + surface->saved_clip = NULL; + } + + surface->extents = surface->clip_rect; + _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); return (cairo_surface_t *)surface; @@ -311,27 +318,6 @@ _cairo_win32_surface_create_similar (void *abstract_src, return _cairo_win32_surface_create_for_dc (src->dc, format, 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, width, height); -} - static cairo_status_t _cairo_win32_surface_finish (void *abstract_surface) { @@ -340,9 +326,8 @@ _cairo_win32_surface_finish (void *abstract_surface) if (surface->image) cairo_surface_destroy (surface->image); - if (surface->saved_clip) { + if (surface->saved_clip) DeleteObject (surface->saved_clip); - } /* If we created the Bitmap and DC, destroy them */ if (surface->bitmap) { @@ -379,8 +364,18 @@ _cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, width, height, surface->dc, x, y, - SRCCOPY)) - goto FAIL; + SRCCOPY)) { + /* If we fail to BitBlt here, most likely the source is a printer. + * You can't reliably get bits from a printer DC, so just fill in + * the surface as white (common case for printing). + */ + + RECT r; + r.left = r.top = 0; + r.right = width; + r.bottom = height; + FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); + } *local_out = local; @@ -403,7 +398,7 @@ _cairo_win32_surface_acquire_source_image (void *abstract_sur 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; @@ -446,7 +441,7 @@ _cairo_win32_surface_acquire_dest_image (void *abstract_surfa cairo_status_t status; RECT clip_box; int x1, y1, x2, y2; - + if (surface->image) { image_rect->x = 0; image_rect->y = 0; @@ -461,12 +456,12 @@ _cairo_win32_surface_acquire_dest_image (void *abstract_surfa 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) @@ -595,6 +590,8 @@ _composite_alpha_blend (cairo_win32_surface_t *dst, if (alpha_blend == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; + if (GetDeviceCaps(dst->dc, SHADEBLENDCAPS) == SB_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; blend_function.BlendOp = AC_SRC_OVER; blend_function.BlendFlags = 0; @@ -634,7 +631,7 @@ _cairo_win32_surface_composite (cairo_operator_t op, int integer_transform; int itx, ity; - if (pattern->type != CAIRO_PATTERN_SURFACE || + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE || pattern->extend != CAIRO_EXTEND_NONE) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -642,7 +639,7 @@ _cairo_win32_surface_composite (cairo_operator_t op, /* 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) + if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; @@ -660,6 +657,33 @@ _cairo_win32_surface_composite (cairo_operator_t op, if (!integer_transform) return CAIRO_INT_STATUS_UNSUPPORTED; + /* Fix up src coordinates; the src coords and size must be within the + * bounds of the source surface. + * XXX the region not covered should be appropriately rendered! + * - for OVER/SOURCE with RGB24 source -> opaque black + * - for SOURCE with ARGB32 source -> 100% transparent black + */ + src_x += itx; + src_y += ity; + + if (src_x < 0) { + width += src_x; + dst_x -= src_x; + src_x = 0; + } + + if (src_y < 0) { + height += src_y; + dst_y -= src_y; + src_y = 0; + } + + if (src_x + width > src->extents.width) + width = src->extents.width - src_x; + + if (src_y + height > src->extents.height) + height = src->extents.height - src_y; + if (alpha == 255 && src->format == dst->format && (op == CAIRO_OPERATOR_SOURCE || @@ -669,7 +693,7 @@ _cairo_win32_surface_composite (cairo_operator_t op, dst_x, dst_y, width, height, src->dc, - src_x + itx, src_y + ity, + src_x, src_y, SRCCOPY)) return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); @@ -681,7 +705,7 @@ _cairo_win32_surface_composite (cairo_operator_t op, op == CAIRO_OPERATOR_OVER) { return _composite_alpha_blend (dst, src, alpha, - src_x + itx, src_y + ity, + src_x, src_y, dst_x, dst_y, width, height); } @@ -855,19 +879,9 @@ _cairo_win32_surface_set_clip_region (void *abstract_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 (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region (reset)"); - if (surface->saved_clip) { - DeleteObject (surface->saved_clip); - surface->saved_clip = NULL; - } - - surface->set_clip = 0; - } - return CAIRO_STATUS_SUCCESS; } else { @@ -910,36 +924,16 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, 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; - } + /* Combine the new region with the original clip */ - if (SelectClipRgn (surface->dc, gdi_region) == ERROR) + if (surface->saved_clip) { + if (CombineRgn (gdi_region, gdi_region, surface->saved_clip, RGN_AND) == 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; } + if (SelectClipRgn (surface->dc, gdi_region) == ERROR) + goto FAIL; + DeleteObject (gdi_region); return CAIRO_STATUS_SUCCESS; @@ -955,15 +949,8 @@ _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_t *rectangle) { 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; + *rectangle = surface->extents; return CAIRO_STATUS_SUCCESS; } @@ -974,11 +961,25 @@ _cairo_win32_surface_flush (void *abstract_surface) return _cairo_surface_reset_clip (abstract_surface); } +/** + * cairo_win32_surface_create: + * @hdc: the DC to create a surface for + * + * Creates a cairo surface that targets the given DC. The DC will be + * queried for its initial clip extents, and this will be used as the + * size of the cairo surface. Also, if the DC is a raster DC, it will + * be queried for its pixel format and the cairo surface format will + * be set appropriately. + * + * Return value: the newly created surface + **/ cairo_surface_t * cairo_win32_surface_create (HDC hdc) { cairo_win32_surface_t *surface; RECT rect; + int depth; + cairo_format_t format; /* Try to figure out the drawing bounds for the Device context */ @@ -988,7 +989,28 @@ cairo_win32_surface_create (HDC hdc) _cairo_error (CAIRO_STATUS_NO_MEMORY); return &_cairo_surface_nil; } - + + if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY) { + depth = GetDeviceCaps(hdc, BITSPIXEL); + if (depth == 32) + format = CAIRO_FORMAT_ARGB32; + else if (depth == 24) + format = CAIRO_FORMAT_RGB24; + else if (depth == 16) + format = CAIRO_FORMAT_RGB24; + else if (depth == 8) + format = CAIRO_FORMAT_A8; + else if (depth == 1) + format = CAIRO_FORMAT_A1; + else { + _cairo_win32_print_gdi_error("cairo_win32_surface_create(bad BITSPIXEL)"); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return &_cairo_surface_nil; + } + } else { + format = CAIRO_FORMAT_RGB24; + } + surface = malloc (sizeof (cairo_win32_surface_t)); if (surface == NULL) { _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -996,7 +1018,7 @@ cairo_win32_surface_create (HDC hdc) } surface->image = NULL; - surface->format = CAIRO_FORMAT_RGB24; + surface->format = format; surface->dc = hdc; surface->bitmap = NULL; @@ -1007,14 +1029,47 @@ cairo_win32_surface_create (HDC hdc) surface->clip_rect.width = rect.right - rect.left; surface->clip_rect.height = rect.bottom - rect.top; - surface->set_clip = 0; - surface->saved_clip = NULL; + if (surface->clip_rect.width == 0 || + surface->clip_rect.height == 0) + { + surface->saved_clip = NULL; + } else { + surface->saved_clip = CreateRectRgn (0, 0, 0, 0); + if (GetClipRgn (hdc, surface->saved_clip) == 0) { + DeleteObject(surface->saved_clip); + surface->saved_clip = NULL; + } + } + + surface->extents = surface->clip_rect; _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); return (cairo_surface_t *)surface; } +/** + * cairo_win32_surface_create_with_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 + * + **/ +cairo_surface_t * +cairo_win32_surface_create_with_dib (cairo_format_t format, + int width, + int height) +{ + return _cairo_win32_surface_create_for_dc (NULL, format, width, height); +} + + /** * _cairo_surface_is_win32: * @surface: a #cairo_surface_t @@ -1029,7 +1084,33 @@ _cairo_surface_is_win32 (cairo_surface_t *surface) return surface->backend == &cairo_win32_surface_backend; } +/** + * cairo_win32_surface_get_dc + * @surface: a #cairo_surface_t + * + * Returns the HDC associated with this surface, or NULL if none. + * Also returns NULL if the surface is not a win32 surface. + * + * Return value: HDC or NULL if no HDC available. + **/ +HDC +cairo_win32_surface_get_dc (cairo_surface_t *surface) +{ + cairo_win32_surface_t *winsurf; + + if (surface == NULL) + return NULL; + + if (!_cairo_surface_is_win32(surface)) + return NULL; + + winsurf = (cairo_win32_surface_t *) surface; + + return winsurf->dc; +} + static const cairo_surface_backend_t cairo_win32_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32, _cairo_win32_surface_create_similar, _cairo_win32_surface_finish, _cairo_win32_surface_acquire_source_image, diff --git a/src/cairo-win32.h b/src/cairo-win32.h index 8d43bb74f..6ba37dec1 100644 --- a/src/cairo-win32.h +++ b/src/cairo-win32.h @@ -47,6 +47,14 @@ CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_win32_surface_create (HDC hdc); +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_dib (cairo_format_t format, + int width, + int height); + +cairo_public HDC +cairo_win32_surface_get_dc (cairo_surface_t *surface); + cairo_public cairo_font_face_t * cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont); diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index faa207c0d..78eafc0fa 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -1025,6 +1025,7 @@ _cairo_xcb_surface_get_extents (void *abstract_surface, } static const cairo_surface_backend_t cairo_xcb_surface_backend = { + CAIRO_SURFACE_TYPE_XCB, _cairo_xcb_surface_create_similar, _cairo_xcb_surface_finish, _cairo_xcb_surface_acquire_source_image, diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 38c7c76b6..0f195bf9b 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -41,6 +41,7 @@ #include "cairo-xlib-test.h" #include "cairo-xlib-private.h" #include +#include /* Xlib doesn't define a typedef, so define one ourselves */ typedef int (*cairo_xlib_error_func_t) (Display *display, @@ -1047,7 +1048,7 @@ _categorize_composite_operation (cairo_xlib_surface_t *dst, if (!dst->buggy_repeat) return DO_RENDER; - if (src_pattern->type == CAIRO_PATTERN_SURFACE) + if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src_pattern; @@ -1675,6 +1676,7 @@ _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font); static const cairo_surface_backend_t cairo_xlib_surface_backend = { + CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_surface_create_similar, _cairo_xlib_surface_finish, _cairo_xlib_surface_acquire_source_image, @@ -2403,6 +2405,8 @@ _cairo_xlib_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_int_status_t status; cairo_xlib_surface_t *self = abstract_surface; cairo_xlib_surface_t *src; + const cairo_glyph_t *glyphs_chunk; + int glyphs_remaining, chunk_size, max_chunk_size; composite_operation_t operation; cairo_scaled_glyph_t *scaled_glyph; cairo_xlib_surface_font_private_t *font_private; @@ -2457,23 +2461,41 @@ _cairo_xlib_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, } _cairo_xlib_surface_ensure_dst_picture (self); - /* Call the appropriate sub-function. */ + max_chunk_size = XMaxRequestSize (self->dpy); if (max_index < 256) - status = _cairo_xlib_surface_old_show_glyphs8 (scaled_font, op, src, self, - source_x + attributes.x_offset - dest_x, - source_y + attributes.y_offset - dest_y, - glyphs, num_glyphs); + max_chunk_size -= sz_xRenderCompositeGlyphs8Req; else if (max_index < 65536) - status = _cairo_xlib_surface_old_show_glyphs16 (scaled_font, op, src, self, - source_x + attributes.x_offset - dest_x, - source_y + attributes.y_offset - dest_y, - glyphs, num_glyphs); - else - status = _cairo_xlib_surface_old_show_glyphs32 (scaled_font, op, src, self, - source_x + attributes.x_offset - dest_x, - source_y + attributes.y_offset - dest_y, - glyphs, num_glyphs); + max_chunk_size -= sz_xRenderCompositeGlyphs16Req; + else + max_chunk_size -= sz_xRenderCompositeGlyphs32Req; + max_chunk_size /= sz_xGlyphElt; + + for (glyphs_remaining = num_glyphs, glyphs_chunk = glyphs; + glyphs_remaining; + glyphs_remaining -= chunk_size, glyphs_chunk += chunk_size) + { + chunk_size = MIN (glyphs_remaining, max_chunk_size); + + /* Call the appropriate sub-function. */ + if (max_index < 256) + status = _cairo_xlib_surface_old_show_glyphs8 (scaled_font, op, src, self, + source_x + attributes.x_offset - dest_x, + source_y + attributes.y_offset - dest_y, + glyphs_chunk, chunk_size); + else if (max_index < 65536) + status = _cairo_xlib_surface_old_show_glyphs16 (scaled_font, op, src, self, + source_x + attributes.x_offset - dest_x, + source_y + attributes.y_offset - dest_y, + glyphs_chunk, chunk_size); + else + status = _cairo_xlib_surface_old_show_glyphs32 (scaled_font, op, src, self, + source_x + attributes.x_offset - dest_x, + source_y + attributes.y_offset - dest_y, + glyphs_chunk, chunk_size); + if (status != CAIRO_STATUS_SUCCESS) + break; + } if (status == CAIRO_STATUS_SUCCESS && !_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_t extents; diff --git a/src/cairo.c b/src/cairo.c index 3319e2b45..0f35fa674 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -220,6 +220,9 @@ cairo_create (cairo_surface_t *target) cairo_t * cairo_reference (cairo_t *cr) { + if (cr == NULL) + return NULL; + if (cr->ref_count == (unsigned int)-1) return cr; @@ -241,6 +244,9 @@ cairo_reference (cairo_t *cr) void cairo_destroy (cairo_t *cr) { + if (cr == NULL) + return; + if (cr->ref_count == (unsigned int)-1) return; @@ -2026,6 +2032,46 @@ cairo_get_font_options (cairo_t *cr, _cairo_gstate_get_font_options (cr->gstate, options); } +/** + * cairo_set_scaled_font: + * @cr: a #cairo_t + * @scaled_font: a #cairo_scaled_font_t + * + * Replaces the current font face, font matrix, and font options in + * the #cairo_t with those of the #cairo_scaled_font_t. Except for + * some translation, the current CTM of the #cairo_t should be the + * same as that of the #cairo_scaled_font_t, which can be accessed + * using cairo_scaled_font_get_ctm(). + **/ +void +cairo_set_scaled_font (cairo_t *cr, + const cairo_scaled_font_t *scaled_font) +{ + if (cr->status) + return; + + cr->status = scaled_font->status; + if (cr->status) + goto BAIL; + + cr->status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); + if (cr->status) + goto BAIL; + + cr->status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); + if (cr->status) + goto BAIL; + + cr->status = _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); + if (cr->status) + goto BAIL; + + return; + +BAIL: + _cairo_set_error (cr, cr->status); +} + /** * cairo_text_extents: * @cr: a #cairo_t @@ -2203,6 +2249,9 @@ cairo_show_glyphs (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs) if (cr->status) return; + if (num_glyphs == 0) + return; + cr->status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, num_glyphs); if (cr->status) _cairo_set_error (cr, cr->status); diff --git a/src/cairo.h b/src/cairo.h index 6dd274267..db9354074 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -855,6 +855,10 @@ cairo_public void cairo_get_font_options (cairo_t *cr, cairo_font_options_t *options); +cairo_public void +cairo_set_scaled_font (cairo_t *cr, + const cairo_scaled_font_t *scaled_font); + cairo_public void cairo_show_text (cairo_t *cr, const char *utf8); @@ -899,6 +903,47 @@ cairo_font_face_destroy (cairo_font_face_t *font_face); cairo_public cairo_status_t cairo_font_face_status (cairo_font_face_t *font_face); +/** + * cairo_font_type_t + * @CAIRO_SCALED_FONT_TYPE_FT: The font is of type ft + * @CAIRO_SCALED_FONT_TYPE_WIN32: The font is of type win32 + * @CAIRO_SCALED_FONT_TYPE_ATSUI: The font is of type atsui + * + * @cairo_font_type_t is used to describe the type of a given font + * face or scaled font. The font types are also known as "font + * backends" within cairo. + * + * The type of a font face is determined by the function used to + * create it, which will generally be of the form + * cairo__font_face_create. The font face type can be queried + * with cairo_font_face_get_type() + * + * The various cairo_font_face functions can be used with a font face + * of any type. + * + * The type of a scaled font is determined by the type of the font + * face passed to cairo_scaled_font_create. The scaled font type can + * be queried with cairo_scaled_font_get_type() + * + * The various cairo_scaled_font functions can be used with scaled + * fonts of any type, but some font backends also provide + * type-specific functions that must only be called with a scaled font + * of the appropriate type. These functions have names that begin with + * cairo__scaled_font such as cairo_ft_scaled_font_lock_face. + * + * The behavior of calling a type-specific function with a scaled font + * of the wrong type is undefined. + */ +typedef enum _cairo_font_type { + CAIRO_FONT_TYPE_TOY, + CAIRO_FONT_TYPE_FT, + CAIRO_FONT_TYPE_WIN32, + CAIRO_FONT_TYPE_ATSUI +} cairo_font_type_t; + +cairo_public cairo_font_type_t +cairo_font_face_get_type (cairo_font_face_t *font_face); + cairo_public void * cairo_font_face_get_user_data (cairo_font_face_t *font_face, const cairo_user_data_key_t *key); @@ -926,6 +971,9 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font); cairo_public cairo_status_t cairo_scaled_font_status (cairo_scaled_font_t *scaled_font); +cairo_public cairo_font_type_t +cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font); + cairo_public void cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *extents); @@ -1150,14 +1198,64 @@ cairo_surface_create_similar (cairo_surface_t *other, cairo_public cairo_surface_t * cairo_surface_reference (cairo_surface_t *surface); +cairo_public void +cairo_surface_finish (cairo_surface_t *surface); + cairo_public void cairo_surface_destroy (cairo_surface_t *surface); cairo_public cairo_status_t cairo_surface_status (cairo_surface_t *surface); -cairo_public void -cairo_surface_finish (cairo_surface_t *surface); +/** + * cairo_surface_type_t + * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image + * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf + * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps + * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib + * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb + * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz + * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz + * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32 + * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos + * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb + * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg + * + * @cairo_surface_type_t is used to describe the type of a given + * surface. The surface types are also known as "backends" or "surface + * backends" within cairo. + * + * The type of a surface is determined by the function used to create + * it, which will generally be of the form cairo__surface_create, + * (though see cairo_surface_create_similar as well). + * + * The surface type can be queried with cairo_surface_get_type() + * + * The various cairo_surface functions can be used with surfaces of + * any type, but some backends also provide type-specific functions + * that must only be called with a surface of the appropriate + * type. These functions have names that begin with + * cairo__surface such as cairo_image_surface_get_width(). + * + * The behavior of calling a type-specific function with a surface of + * the wrong type is undefined. + */ +typedef enum _cairo_surface_type { + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_SURFACE_TYPE_PDF, + CAIRO_SURFACE_TYPE_PS, + CAIRO_SURFACE_TYPE_XLIB, + CAIRO_SURFACE_TYPE_XCB, + CAIRO_SURFACE_TYPE_GLITZ, + CAIRO_SURFACE_TYPE_QUARTZ, + CAIRO_SURFACE_TYPE_WIN32, + CAIRO_SURFACE_TYPE_BEOS, + CAIRO_SURFACE_TYPE_DIRECTFB, + CAIRO_SURFACE_TYPE_SVG +} cairo_surface_type_t; + +cairo_public cairo_surface_type_t +cairo_surface_get_type (cairo_surface_t *surface); #if CAIRO_HAS_PNG_FUNCTIONS @@ -1293,6 +1391,43 @@ cairo_pattern_destroy (cairo_pattern_t *pattern); cairo_public cairo_status_t cairo_pattern_status (cairo_pattern_t *pattern); +/** + * cairo_pattern_type_t + + * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform) + * color. It may be opaque or translucent. + * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image). + * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient. + * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient. + * + * @cairo_pattern_type_t us used to describe the type of a given pattern. + * + * The type of a pattern is determined by the function used to create + * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba() + * functions create SOLID patterns. The remaining + * cairo_pattern_create functions map to pattern types in obvious + * ways. + * + * The pattern type can be queried with cairo_pattern_get_type() + * + * Most cairo_pattern functions can be called with a pattern of any + * type, (though trying to change the extend or filter for a solid + * pattern will have no effect). A notable exception is + * cairo_pattern_add_color_stop_rgb() and + * cairo_pattern_add_color_stop_rgba() which must only be called with + * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern + * will be shutdown and put into an error state. + */ +typedef enum _cairo_pattern_type { + CAIRO_PATTERN_TYPE_SOLID, + CAIRO_PATTERN_TYPE_SURFACE, + CAIRO_PATTERN_TYPE_LINEAR, + CAIRO_PATTERN_TYPE_RADIAL +} cairo_pattern_type_t; + +cairo_public cairo_pattern_type_t +cairo_pattern_get_type (cairo_pattern_t *pattern); + cairo_public void cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double offset, diff --git a/src/cairoint.h b/src/cairoint.h index d51b994cf..65b482ff9 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -184,6 +184,10 @@ cairo_private void _cairo_beos_unlock(void*); #define TRUE 1 #endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + #define ASSERT_NOT_REACHED \ do { \ static const int NOT_REACHED = 0; \ @@ -256,6 +260,15 @@ typedef enum cairo_int_status { CAIRO_INT_STATUS_CACHE_EMPTY } cairo_int_status_t; +typedef enum cairo_internal_surface_type { + CAIRO_INTERNAL_SURFACE_TYPE_META = 0x1000, + CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, + CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED +} cairo_internal_surface_type_t; + typedef enum cairo_direction { CAIRO_DIRECTION_FORWARD, CAIRO_DIRECTION_REVERSE @@ -464,6 +477,7 @@ struct _cairo_scaled_font { }; struct _cairo_font_face { + /* hash_entry must be first */ cairo_hash_entry_t hash_entry; cairo_status_t status; int ref_count; @@ -508,6 +522,8 @@ typedef enum _cairo_scaled_glyph_info { } cairo_scaled_glyph_info_t; struct _cairo_scaled_font_backend { + cairo_font_type_t type; + cairo_status_t (*create_toy) (cairo_toy_font_face_t *toy_face, const cairo_matrix_t *font_matrix, @@ -555,6 +571,8 @@ struct _cairo_scaled_font_backend { }; struct _cairo_font_face_backend { + cairo_font_type_t type; + /* The destroy() function is allowed to resurrect the font face * by re-referencing. This is needed for the FreeType backend. */ @@ -599,6 +617,8 @@ typedef struct _cairo_stroke_style { } cairo_stroke_style_t; struct _cairo_surface_backend { + cairo_surface_type_t type; + cairo_surface_t * (*create_similar) (void *surface, cairo_content_t content, @@ -829,6 +849,11 @@ typedef struct _cairo_format_masks { struct _cairo_surface { const cairo_surface_backend_t *backend; + /* We allow surfaces to override the backend->type by shoving something + * else into surface->type. This is for "wrapper" surfaces that want to + * hide their internal type from the user-level API. */ + cairo_surface_type_t type; + unsigned int ref_count; cairo_status_t status; cairo_bool_t finished; @@ -909,13 +934,6 @@ typedef enum { #define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_BEST -typedef enum { - CAIRO_PATTERN_SOLID, - CAIRO_PATTERN_SURFACE, - CAIRO_PATTERN_LINEAR, - CAIRO_PATTERN_RADIAL -} cairo_pattern_type_t; - struct _cairo_pattern { cairo_pattern_type_t type; unsigned int ref_count; @@ -1372,6 +1390,13 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, cairo_private cairo_status_t _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices); +/* cairo_operator.c */ +cairo_private cairo_bool_t +_cairo_operator_always_opaque (cairo_operator_t op); + +cairo_private cairo_bool_t +_cairo_operator_always_translucent (cairo_operator_t op); + /* cairo_path.c */ cairo_private void _cairo_path_fixed_init (cairo_path_fixed_t *path); @@ -1536,7 +1561,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_private cairo_status_t _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, + const cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path); @@ -1804,6 +1829,9 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, unsigned int width, unsigned int height); +cairo_private cairo_bool_t +_cairo_surface_is_opaque (const cairo_surface_t *surface); + /* cairo_image_surface.c */ #define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ @@ -1847,6 +1875,19 @@ _cairo_image_surface_create_for_data_with_content (unsigned char *data, cairo_private void _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); +/* XXX: It's a nasty kludge that this appears here. Backend functions + * like this should really be static. But we're doing this to work + * around some general defects in the backend clipping interfaces, + * (see some notes in test-paginated-surface.c). + * + * I want to fix the real defects, but it's "hard" as they touch many + * backends, so doing that will require synchronizing several backend + * maintainers. + */ +cairo_private cairo_int_status_t +_cairo_image_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region); + cairo_private cairo_bool_t _cairo_surface_is_image (const cairo_surface_t *surface); @@ -2040,7 +2081,10 @@ _cairo_pattern_transform (cairo_pattern_t *pattern, const cairo_matrix_t *ctm_inverse); cairo_private cairo_bool_t -_cairo_pattern_is_opaque_solid (cairo_pattern_t *pattern); +_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); + +cairo_bool_t +_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern); cairo_private cairo_int_status_t _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, @@ -2110,14 +2154,37 @@ _cairo_utf8_to_utf16 (const unsigned char *str, typedef struct _cairo_output_stream cairo_output_stream_t; +extern const cairo_private cairo_output_stream_t cairo_output_stream_nil; + +/* We already have the following declared in cairo.h: + +typedef cairo_status_t (*cairo_write_func_t) (void *closure, + const unsigned char *data, + unsigned int length); +*/ +typedef cairo_status_t (*cairo_close_func_t) (void *closure); + + +/* This function never returns NULL. If an error occurs (NO_MEMORY) + * while trying to create the output stream this function returns a + * valid pointer to a nil output stream. + * + * Note that even with a nil surface, the close_func callback will be + * called by a call to _cairo_output_stream_close or + * _cairo_output_stream_destroy. + */ cairo_private cairo_output_stream_t * _cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, void *closure); +cairo_private void +_cairo_output_stream_close (cairo_output_stream_t *stream); + cairo_private void _cairo_output_stream_destroy (cairo_output_stream_t *stream); -cairo_private cairo_status_t +cairo_private void _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length); @@ -2126,11 +2193,14 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, const char *data, size_t length); -cairo_private cairo_status_t +cairo_private unsigned char * +_cairo_lzw_compress (unsigned char *data, unsigned long *data_size_in_out); + +cairo_private void _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *fmt, va_list ap); -cairo_private cairo_status_t +cairo_private void _cairo_output_stream_printf (cairo_output_stream_t *stream, const char *fmt, ...) CAIRO_PRINTF_FORMAT(2, 3); @@ -2140,8 +2210,30 @@ _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); +/* This function never returns NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * NOTE: Even if a nil surface is returned, the caller should still + * call _cairo_output_stream_destroy (or _cairo_output_stream_close at + * least) in order to ensure that everything is properly cleaned up. + */ cairo_private cairo_output_stream_t * -_cairo_output_stream_create_for_file (const char *filename); +_cairo_output_stream_create_for_filename (const char *filename); + +/* This function never returns NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * The caller still "owns" file and is responsible for calling fclose + * on it when finished. The stream will not do this itself. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_for_file (FILE *file); + +/* cairo_base85_stream.c */ +cairo_output_stream_t * +_cairo_base85_stream_create (cairo_output_stream_t *output); cairo_private void _cairo_error (cairo_status_t status); @@ -2150,7 +2242,6 @@ cairo_private int _cairo_dtostr (char *buffer, size_t size, double d); /* Avoid unnecessary PLT entries. */ - slim_hidden_proto(cairo_get_current_point) slim_hidden_proto(cairo_fill_preserve) slim_hidden_proto(cairo_clip_preserve) diff --git a/src/test-fallback-surface.c b/src/test-fallback-surface.c index fe0cc6fb5..cb8fd92a0 100644 --- a/src/test-fallback-surface.c +++ b/src/test-fallback-surface.c @@ -175,6 +175,7 @@ _test_fallback_surface_get_extents (void *abstract_surface, } const cairo_surface_backend_t test_fallback_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, _test_fallback_surface_create_similar, _test_fallback_surface_finish, _test_fallback_surface_acquire_source_image, diff --git a/src/test-meta-surface.c b/src/test-meta-surface.c index bdabf31d3..6d36fcab2 100644 --- a/src/test-meta-surface.c +++ b/src/test-meta-surface.c @@ -296,6 +296,7 @@ _test_meta_surface_snapshot (void *abstract_other) } const cairo_surface_backend_t test_meta_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, NULL, /* create_similar */ _test_meta_surface_finish, _test_meta_surface_acquire_source_image, diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c index 155f2773f..e4eb58a2d 100644 --- a/src/test-paginated-surface.c +++ b/src/test-paginated-surface.c @@ -51,6 +51,15 @@ #include "cairo-paginated-surface-private.h" +typedef struct _test_paginated_surface { + cairo_surface_t base; + cairo_surface_t *target; + cairo_paginated_mode_t paginated_mode; +} test_paginated_surface_t; + +static const cairo_surface_backend_t test_paginated_surface_backend; +static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend; + cairo_surface_t * _test_paginated_surface_create_for_data (unsigned char *data, cairo_content_t content, @@ -58,11 +67,218 @@ _test_paginated_surface_create_for_data (unsigned char *data, int height, int stride) { + cairo_status_t status; cairo_surface_t *target; + test_paginated_surface_t *surface; - target = _cairo_image_surface_create_for_data_with_content (data, content, + target = _cairo_image_surface_create_for_data_with_content (data, content, width, height, stride); + status = cairo_surface_status (target); + if (status) { + _cairo_error (status); + return (cairo_surface_t *) &_cairo_surface_nil; + } - return _cairo_paginated_surface_create (target, content, width, height); + surface = malloc (sizeof (test_paginated_surface_t)); + if (surface == NULL) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + _cairo_surface_init (&surface->base, &test_paginated_surface_backend); + + surface->target = target; + + return _cairo_paginated_surface_create (&surface->base, content, width, height, + &test_paginated_surface_paginated_backend); } + +static cairo_int_status_t +_test_paginated_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + /* XXX: The whole surface backend clipping interface is a giant + * disaster right now. In particular, its uncleanness shows up + * when trying to implement one surface that wraps another one (as + * we are doing here). + * + * Here are two of the problems that show up: + * + * 1. The most critical piece of information in all this stuff, + * the "clip" isn't getting passed to the backend + * functions. Instead the generic surface layer is caching that as + * surface->clip. This is a problem for surfaces like this one + * that do wrapping. Our base surface will have the clip set, but + * our target's surface will not. + * + * 2. We're here in our backend's set_clip_region function, and we + * want to call into our target surface's set_clip_region. + * Generally, we would do this by calling an equivalent + * _cairo_surface function, but _cairo_surface_set_clip_region + * does not have the same signature/semantics, (it has the + * clip_serial stuff as well). + * + * We kludge around each of these by manually copying the clip + * object from our base surface into the target's base surface + * (yuck!) and by reaching directly into the image surface's + * set_clip_region instead of calling into the generic + * _cairo_surface_set_clip_region (double yuck!). + */ + + surface->target->clip = surface->base.clip; + + return _cairo_image_surface_set_clip_region (surface->target, region); +} + +static cairo_int_status_t +_test_paginated_surface_get_extents (void *abstract_surface, + cairo_rectangle_t *rectangle) +{ + test_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static cairo_int_status_t +_test_paginated_surface_paint (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_paint (surface->target, op, source); +} + +static cairo_int_status_t +_test_paginated_surface_mask (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_pattern_t *mask) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_mask (surface->target, op, source, mask); +} + +static cairo_int_status_t +_test_paginated_surface_stroke (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_stroke (surface->target, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias); +} + +static cairo_int_status_t +_test_paginated_surface_fill (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_fill (surface->target, op, source, + path, fill_rule, + tolerance, antialias); +} + +static cairo_int_status_t +_test_paginated_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + cairo_pattern_t *source, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_show_glyphs (surface->target, op, source, + glyphs, num_glyphs, scaled_font); +} + +static void +_test_paginated_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t mode) +{ + test_paginated_surface_t *surface = abstract_surface; + + surface->paginated_mode = mode; +} + +static const cairo_surface_backend_t test_paginated_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + + /* Since we are a paginated user, we get to regard most of the + * surface backend interface as historical cruft and ignore it. */ + + NULL, /* create_similar */ + NULL, /* finish */ + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* copy_page */ + NULL, /* show_page */ + _test_paginated_surface_set_clip_region, + NULL, /* intersect_clip_path */ + _test_paginated_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ + NULL, /* scaled_glyph_fini */ + + /* Here is the more "modern" section of the surface backend + * interface which is mostly just drawing functions */ + + _test_paginated_surface_paint, + _test_paginated_surface_mask, + _test_paginated_surface_stroke, + _test_paginated_surface_fill, + _test_paginated_surface_show_glyphs, + NULL /* snapshot */ +}; + +static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = { + NULL, /* start_page */ + _test_paginated_surface_set_paginated_mode +}; diff --git a/test/.gitignore b/test/.gitignore index 1a1212224..c124fc60b 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -19,11 +19,13 @@ create-from-png create-from-png-stream dash-caps-joins dash-offset-negative +dash-zero-length extend-reflect fill-and-stroke fill-rule filter-nearest-offset ft-font-create-for-ft-face +font-face-get-type get-and-set gradient-alpha imagediff @@ -44,6 +46,7 @@ operator-source paint paint-with-alpha path-data +pattern-get-type pdf2png png-flatten svg2png @@ -60,6 +63,7 @@ select-font-no-show-text self-copy self-intersecting set-source +show-glyphs-many show-text-current-point source-clip source-surface-scale-paint diff --git a/test/Makefile.am b/test/Makefile.am index 0ea0624aa..044dc08c1 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,4 @@ -# All test cases go here +# Here are all the tests that are run unconditionally TESTS = \ a8-mask \ caps-joins \ @@ -16,10 +16,12 @@ create-from-png \ create-from-png-stream \ dash-caps-joins \ dash-offset-negative \ +dash-zero-length \ extend-reflect \ fill-and-stroke \ fill-rule \ filter-nearest-offset \ +font-face-get-type \ get-and-set \ gradient-alpha \ leaky-polygon \ @@ -36,6 +38,7 @@ operator-source \ paint \ paint-with-alpha \ path-data \ +pattern-get-type \ pixman-rotate \ rectangle-rounding-error \ scale-source-surface-paint \ @@ -62,6 +65,8 @@ unbounded-operator \ user-data \ rel-path +# Then we have a collection of tests that are only run if certain +# features are compiled into cairo if HAVE_PTHREAD TESTS += pthread-show-text endif @@ -82,6 +87,23 @@ if CAIRO_HAS_MULTI_PAGE_SURFACES TESTS += multi-page endif +# XXX: Here are some existing tests that are currently disabled for +# one reason or another. +# +# show-glyphs-many - this stress test was exercising a particular bug +# in the xlib surface code (exceeding the X11 protocol request +# limit) when rendering several thousand glyphs at once. The +# original xlib-surface bug is fixed now, but the test continues +# to stress some other aspects of the test suite. For example, +# when doing text as paths, the resuilting PostScript file is one +# giant path that ghostscript has a particularly hard time +# with. I'm disabling this test for now, since I don't care about +# that performance problem in ghostscript. (But, there is a +# similar performance problem when using cairo to rasterize the +# equivalen giant path---from an SBG files say---so this might be +# a useful kind of test to bring back again for performance (not +# correctness) testing. + # All tests which have a reference image go here. # I really don't like having to repeat this list. Anyone know a good # way to avoid it? Can I use a wildcard here? @@ -120,6 +142,8 @@ dash-caps-joins-rgb24-ref.png \ dash-offset-negative-ref.png \ dash-offset-negative-rgb24-ref.png \ dash-offset-negative-ps-rgb24-ref.png \ +dash-zero-length-ref.png \ +dash-zero-length-rgb24-ref.png \ extend-reflect-ref.png \ extend-reflect-rgb24-ref.png \ fill-and-stroke-ref.png \ @@ -299,9 +323,11 @@ create_from_png_LDADD = $(LDADDS) create_from_png_stream_LDADD = $(LDADDS) dash_caps_joins_LDADD = $(LDADDS) dash_offset_negative_LDADD = $(LDADDS) +dash_zero_length_LDADD = $(LDADDS) extend_reflect_LDADD = $(LDADDS) fill_and_stroke_LDADD = $(LDADDS) fill_rule_LDADD = $(LDADDS) +font_face_get_type_LDADD = $(LDADDS) filter_nearest_offset_LDADD = $(LDADDS) ft_font_create_for_ft_face_LDADD = $(LDADDS) get_and_set_LDADD = $(LDADDS) @@ -321,6 +347,7 @@ operator_source_LDADD = $(LDADDS) paint_LDADD = $(LDADDS) paint_with_alpha_LDADD = $(LDADDS) path_data_LDADD = $(LDADDS) +pattern_get_type_LDADD = $(LDADDS) svg_surface_LDADD = $(LDADDS) svg_clip_LDADD = $(LDADDS) pixman_rotate_LDADD = $(LDADDS) diff --git a/test/cairo-test.c b/test/cairo-test.c index 557ccb6fd..fce38fe48 100644 --- a/test/cairo-test.c +++ b/test/cairo-test.c @@ -47,6 +47,21 @@ #include "write-png.h" #include "xmalloc.h" +/* This is copied from cairoint.h. That makes it painful to keep in + * sync, but the slim stuff makes cairoint.h "hard" to include when + * not actually building the cairo library itself. Fortunately, since + * we're checking all these values, we do have a safeguard for keeping + * them in sync. + */ +typedef enum cairo_internal_surface_type { + CAIRO_INTERNAL_SURFACE_TYPE_META = 0x1000, + CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, + CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED +} cairo_internal_surface_type_t; + #ifdef _MSC_VER #define vsnprintf _vsnprintf #define access _access @@ -163,11 +178,12 @@ typedef void typedef struct _cairo_test_target { const char *name; + cairo_surface_type_t expected_type; cairo_content_t content; cairo_test_create_target_surface_t create_target_surface; cairo_test_write_to_png_t write_to_png; cairo_test_cleanup_target_t cleanup_target; - void *closure; + void *closure; } cairo_test_target_t; static char * @@ -1282,12 +1298,21 @@ cleanup_pdf (void *closure) #if CAIRO_HAS_SVG_SURFACE && CAIRO_CAN_TEST_SVG_SURFACE #include "cairo-svg.h" +static const char *svg_ignored_tests[] = { + "operator-source", + "operator-clear", + "clip-operator", + "unbounded-operator", + NULL +}; + cairo_user_data_key_t svg_closure_key; typedef struct _svg_target_closure { char *filename; int width, height; + cairo_surface_t *target; } svg_target_closure_t; static cairo_surface_t * @@ -1297,22 +1322,41 @@ create_svg_surface (cairo_test_t *test, { int width = test->width; int height = test->height; + int i; svg_target_closure_t *ptc; cairo_surface_t *surface; + for (i = 0; svg_ignored_tests[i] != NULL; i++) + if (strcmp (test->name, svg_ignored_tests[i]) == 0) + return NULL; + *closure = ptc = xmalloc (sizeof (svg_target_closure_t)); ptc->width = width; ptc->height = height; - xasprintf (&ptc->filename, "%s-%s%s", test->name, "svg-argb32-out", ".svg"); + xasprintf (&ptc->filename, "%s-svg-%s-out.svg", + test->name, _cairo_test_content_name (content)); + surface = cairo_svg_surface_create (ptc->filename, width, height); if (cairo_surface_status (surface)) { free (ptc->filename); free (ptc); return NULL; } + cairo_svg_surface_set_dpi (surface, 72., 72.); + + if (content == CAIRO_CONTENT_COLOR) { + ptc->target = surface; + surface = cairo_surface_create_similar (ptc->target, + CAIRO_CONTENT_COLOR, + width, height); + } else { + ptc->target = NULL; + } + cairo_surface_set_user_data (surface, &svg_closure_key, ptc, NULL); + return surface; } @@ -1322,15 +1366,26 @@ svg_surface_write_to_png (cairo_surface_t *surface, const char *filename) svg_target_closure_t *ptc = cairo_surface_get_user_data (surface, &svg_closure_key); char command[4096]; - cairo_surface_finish (surface); + if (ptc->target) { + cairo_t *cr; + cr = cairo_create (ptc->target); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + cairo_show_page (cr); + cairo_destroy (cr); + cairo_surface_finish (surface); + surface = ptc->target; + } + + cairo_surface_finish (surface); sprintf (command, "./svg2png %s %s", ptc->filename, filename); if (system (command) != 0) return CAIRO_STATUS_WRITE_ERROR; - return CAIRO_STATUS_WRITE_ERROR; + return CAIRO_STATUS_SUCCESS; } static void @@ -1387,13 +1442,19 @@ cairo_test_for_target (cairo_test_t *test, goto UNWIND_STRINGS; } + if (cairo_surface_get_type (surface) != target->expected_type) { + cairo_test_log ("Error: Created surface is of type %d (expected %d)\n", + cairo_surface_get_type (surface), target->expected_type); + ret = CAIRO_TEST_FAILURE; + goto UNWIND_SURFACE; + } + cr = cairo_create (surface); /* Clear to transparent (or black) depending on whether the target * surface supports alpha. */ cairo_save (cr); - cairo_set_source_rgba (cr, 0, 0, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); @@ -1418,6 +1479,7 @@ cairo_test_for_target (cairo_test_t *test, /* Skip image check for tests with no image (width,height == 0,0) */ if (test->width != 0 && test->height != 0) { int pixels_changed; + xunlink (png_name); (target->write_to_png) (surface, png_name); if (target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED) pixels_changed = image_diff_flattened (png_name, ref_name, diff_name); @@ -1436,6 +1498,7 @@ cairo_test_for_target (cairo_test_t *test, UNWIND_CAIRO: cairo_destroy (cr); +UNWIND_SURFACE: cairo_surface_destroy (surface); cairo_debug_reset_static_data (); @@ -1461,102 +1524,134 @@ cairo_test_expecting (cairo_test_t *test, cairo_test_draw_function_t draw, cairo_test_target_t **targets_to_test; cairo_test_target_t targets[] = { - { "image", CAIRO_CONTENT_COLOR_ALPHA, + { "image", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, create_image_surface, cairo_surface_write_to_png, NULL}, - { "image", CAIRO_CONTENT_COLOR, + { "image", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, create_image_surface, cairo_surface_write_to_png, NULL}, #ifdef CAIRO_HAS_TEST_SURFACES - { "test-fallback", CAIRO_CONTENT_COLOR_ALPHA, + { "test-fallback", CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_CONTENT_COLOR_ALPHA, create_test_fallback_surface, cairo_surface_write_to_png, NULL }, - { "test-fallback", CAIRO_CONTENT_COLOR, + { "test-fallback", CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_CONTENT_COLOR, create_test_fallback_surface, cairo_surface_write_to_png, NULL }, - { "test-meta", CAIRO_CONTENT_COLOR_ALPHA, + { "test-meta", CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, + CAIRO_CONTENT_COLOR_ALPHA, create_test_meta_surface, cairo_surface_write_to_png, NULL }, - { "test-meta", CAIRO_CONTENT_COLOR, + { "test-meta", CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, + CAIRO_CONTENT_COLOR, create_test_meta_surface, cairo_surface_write_to_png, NULL }, - { "test-paginated", CAIRO_CONTENT_COLOR_ALPHA, + { "test-paginated", CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + CAIRO_CONTENT_COLOR_ALPHA, create_test_paginated_surface, test_paginated_write_to_png, cleanup_test_paginated }, - { "test-paginated", CAIRO_CONTENT_COLOR, + { "test-paginated", CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + CAIRO_CONTENT_COLOR, create_test_paginated_surface, test_paginated_write_to_png, cleanup_test_paginated }, #endif #ifdef CAIRO_HAS_GLITZ_SURFACE #if CAIRO_CAN_TEST_GLITZ_GLX_SURFACE - { "glitz-glx", CAIRO_CONTENT_COLOR_ALPHA, + { "glitz-glx", CAIRO_SURFACE_TYPE_GLITZ,CAIRO_CONTENT_COLOR_ALPHA, create_cairo_glitz_glx_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_glx }, - { "glitz-glx", CAIRO_CONTENT_COLOR, + { "glitz-glx", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, create_cairo_glitz_glx_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_glx }, #endif #if CAIRO_CAN_TEST_GLITZ_AGL_SURFACE - { "glitz-agl", CAIRO_CONTENT_COLOR_ALPHA, + { "glitz-agl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR_ALPHA, create_cairo_glitz_agl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_agl }, - { "glitz-agl", CAIRO_CONTENT_COLOR, + { "glitz-agl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, create_cairo_glitz_agl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_agl }, #endif #if CAIRO_CAN_TEST_GLITZ_WGL_SURFACE - { "glitz-wgl", CAIRO_CONTENT_COLOR_ALPHA, + { "glitz-wgl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR_ALPHA, create_cairo_glitz_wgl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_wgl }, - { "glitz-wgl", CAIRO_CONTENT_COLOR, + { "glitz-wgl", CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, create_cairo_glitz_wgl_surface, cairo_surface_write_to_png, cleanup_cairo_glitz_wgl }, #endif #endif /* CAIRO_HAS_GLITZ_SURFACE */ #if 0 && CAIRO_HAS_QUARTZ_SURFACE - { "quartz", CAIRO_CONTENT_COLOR, + { "quartz", CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR, create_quartz_surface, cairo_surface_write_to_png, cleanup_quartz }, #endif #if CAIRO_HAS_WIN32_SURFACE - { "win32", CAIRO_CONTENT_COLOR, + { "win32", CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR, create_win32_surface, cairo_surface_write_to_png, cleanup_win32 }, #endif #if CAIRO_HAS_XCB_SURFACE - { "xcb", CAIRO_CONTENT_COLOR_ALPHA, + { "xcb", CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR_ALPHA, create_xcb_surface, cairo_surface_write_to_png, cleanup_xcb}, #endif #if CAIRO_HAS_XLIB_SURFACE - { "xlib", CAIRO_CONTENT_COLOR_ALPHA, + { "xlib", CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR_ALPHA, create_xlib_surface, cairo_surface_write_to_png, cleanup_xlib}, - { "xlib", CAIRO_CONTENT_COLOR, + { "xlib", CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, create_xlib_surface, cairo_surface_write_to_png, cleanup_xlib}, #endif #if CAIRO_HAS_PS_SURFACE - { "ps", CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, + { "ps", CAIRO_SURFACE_TYPE_PS, + CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, create_ps_surface, ps_surface_write_to_png, cleanup_ps }, - { "ps", CAIRO_CONTENT_COLOR, + + /* XXX: We expect type image here only due to a limitation in + * the current PS/meta-surface code. A PS surface is + * "naturally" COLOR_ALPHA, so the COLOR-only variant goes + * through create_similar in create_ps_surface which results + * in the similar surface being used as a source. We do not yet + * have source support for PS/meta-surfaces, so the + * create_similar path for all paginated surfaces currently + * returns an image surface.*/ + { "ps", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, create_ps_surface, ps_surface_write_to_png, cleanup_ps }, #endif #if CAIRO_HAS_PDF_SURFACE && CAIRO_CAN_TEST_PDF_SURFACE - { "pdf", CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, + { "pdf", CAIRO_SURFACE_TYPE_PDF, + CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED, create_pdf_surface, pdf_surface_write_to_png, cleanup_pdf }, - { "pdf", CAIRO_CONTENT_COLOR, + + /* XXX: We expect type image here only due to a limitation in + * the current PDF/meta-surface code. A PDF surface is + * "naturally" COLOR_ALPHA, so the COLOR-only variant goes + * through create_similar in create_pdf_surface which results + * in the similar surface being used as a source. We do not yet + * have source support for PDF/meta-surfaces, so the + * create_similar path for all paginated surfaces currently + * returns an image surface.*/ + { "pdf", CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, create_pdf_surface, pdf_surface_write_to_png, cleanup_pdf }, #endif #if CAIRO_HAS_SVG_SURFACE && CAIRO_CAN_TEST_SVG_SURFACE - { "svg", CAIRO_CONTENT_COLOR_ALPHA, + { "svg", CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, + create_svg_surface, svg_surface_write_to_png, cleanup_svg }, + + /* A SVG surface is COLOR_APLHA by default, and currently a create + * similar with content != COLOR_ALPHA will return a nil surface. + * So don't test COLOR for now. */ + { "svg", CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR, create_svg_surface, svg_surface_write_to_png, cleanup_svg }, #endif #if CAIRO_HAS_BEOS_SURFACE - { "beos", CAIRO_CONTENT_COLOR, + { "beos", CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR, create_beos_surface, cairo_surface_write_to_png, cleanup_beos}, - { "beos_bitmap", CAIRO_CONTENT_COLOR, + { "beos_bitmap", CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR, create_beos_bitmap_surface, cairo_surface_write_to_png, cleanup_beos_bitmap}, - { "beos_bitmap", CAIRO_CONTENT_COLOR_ALPHA, + { "beos_bitmap", CAIRO_SURFACE_TYPE_BEOS, CAIRO_CONTENT_COLOR_ALPHA, create_beos_bitmap_surface, cairo_surface_write_to_png, cleanup_beos_bitmap}, #endif #if CAIRO_HAS_DIRECTFB_SURFACE - { "directfb", CAIRO_CONTENT_COLOR, + { "directfb", CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR, create_directfb_surface, cairo_surface_write_to_png, cleanup_directfb}, - { "directfb_bitmap", CAIRO_CONTENT_COLOR_ALPHA, + { "directfb_bitmap", CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR_ALPHA, create_directfb_bitmap_surface, cairo_surface_write_to_png,cleanup_directfb}, #endif }; diff --git a/test/caps-joins-ps-argb32-ref.png b/test/caps-joins-ps-argb32-ref.png new file mode 100644 index 000000000..d6742957a Binary files /dev/null and b/test/caps-joins-ps-argb32-ref.png differ diff --git a/test/caps-sub-paths-ps-argb32-ref.png b/test/caps-sub-paths-ps-argb32-ref.png new file mode 100644 index 000000000..a7bc1acb4 Binary files /dev/null and b/test/caps-sub-paths-ps-argb32-ref.png differ diff --git a/test/clip-fill-rule-ps-argb32-ref.png b/test/clip-fill-rule-ps-argb32-ref.png new file mode 100644 index 000000000..6d97e04de Binary files /dev/null and b/test/clip-fill-rule-ps-argb32-ref.png differ diff --git a/test/clip-nesting-ps-argb32-ref.png b/test/clip-nesting-ps-argb32-ref.png new file mode 100644 index 000000000..1bd43d7a1 Binary files /dev/null and b/test/clip-nesting-ps-argb32-ref.png differ diff --git a/test/clip-twice-ps-argb32-ref.png b/test/clip-twice-ps-argb32-ref.png new file mode 100644 index 000000000..9ec76b4ef Binary files /dev/null and b/test/clip-twice-ps-argb32-ref.png differ diff --git a/test/dash-caps-joins-ps-argb32-ref.png b/test/dash-caps-joins-ps-argb32-ref.png new file mode 100644 index 000000000..629b97b34 Binary files /dev/null and b/test/dash-caps-joins-ps-argb32-ref.png differ diff --git a/test/dash-caps-joins-ref.png b/test/dash-caps-joins-ref.png index 5c887c5c9..9218ae499 100644 Binary files a/test/dash-caps-joins-ref.png and b/test/dash-caps-joins-ref.png differ diff --git a/test/dash-caps-joins-rgb24-ref.png b/test/dash-caps-joins-rgb24-ref.png index 28339f8ed..6ec274dfb 100644 Binary files a/test/dash-caps-joins-rgb24-ref.png and b/test/dash-caps-joins-rgb24-ref.png differ diff --git a/test/dash-offset-negative-ps-argb32-ref.png b/test/dash-offset-negative-ps-argb32-ref.png new file mode 100644 index 000000000..518e1c925 Binary files /dev/null and b/test/dash-offset-negative-ps-argb32-ref.png differ diff --git a/test/dash-zero-length-ps-argb32-ref.png b/test/dash-zero-length-ps-argb32-ref.png new file mode 100644 index 000000000..5c7fab579 Binary files /dev/null and b/test/dash-zero-length-ps-argb32-ref.png differ diff --git a/test/dash-zero-length-ref.png b/test/dash-zero-length-ref.png new file mode 100644 index 000000000..aca856f2b Binary files /dev/null and b/test/dash-zero-length-ref.png differ diff --git a/test/dash-zero-length-rgb24-ref.png b/test/dash-zero-length-rgb24-ref.png new file mode 100644 index 000000000..f5e40e85c Binary files /dev/null and b/test/dash-zero-length-rgb24-ref.png differ diff --git a/test/dash-zero-length.c b/test/dash-zero-length.c new file mode 100644 index 000000000..a5d0047de --- /dev/null +++ b/test/dash-zero-length.c @@ -0,0 +1,88 @@ +/* + * Copyright © 2006 Jeff Muizelaar + * + * 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 + * Jeff Muizelaar. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Jeff Muizelaar. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL JEFF MUIZELAAR 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: Jeff Muizelaar + */ + +#include "cairo-test.h" + +#define IMAGE_WIDTH 19 +#define IMAGE_HEIGHT 25 + +/* A test of the two extremes of dashing: a solid line + * and an invisible one. Also test that capping works + * on invisible lines. + */ + +cairo_test_t test = { + "dash-zero-length", + "Tests cairo_set_dash with zero length", + IMAGE_WIDTH, IMAGE_HEIGHT +}; + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + double solid_line[] = { 4, 0 }; + double invisible_line[] = { 0, 4 }; + double dotted_line[] = { 0, 6 }; + double rounded_line[] = { 2, 6 }; + + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_set_line_width (cr, 2); + + /* draw a solid line */ + cairo_set_dash (cr, solid_line, 2, 0); + cairo_move_to (cr, 1, 2); + cairo_line_to (cr, 18, 2); + cairo_stroke (cr); + + /* draw an invisible line */ + cairo_set_dash (cr, invisible_line, 2, 0); + cairo_move_to (cr, 1, 8); + cairo_line_to (cr, 18, 8); + cairo_stroke (cr); + + /* draw a dotted line */ + cairo_set_line_width (cr, 5); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_dash (cr, dotted_line, 2, 0); + cairo_move_to (cr, 5, 13); + cairo_line_to (cr, 18, 13); + cairo_stroke (cr); + + /* draw a rounded line */ + cairo_set_line_width (cr, 5); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_dash (cr, rounded_line, 2, 2); + cairo_move_to (cr, 5, 20); + cairo_line_to (cr, 18, 20); + cairo_stroke (cr); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test, draw); +} diff --git a/test/fill-and-stroke-ps-argb32-ref.png b/test/fill-and-stroke-ps-argb32-ref.png new file mode 100644 index 000000000..0df3205df Binary files /dev/null and b/test/fill-and-stroke-ps-argb32-ref.png differ diff --git a/test/fill-rule-ps-argb32-ref.png b/test/fill-rule-ps-argb32-ref.png new file mode 100644 index 000000000..b5487e683 Binary files /dev/null and b/test/fill-rule-ps-argb32-ref.png differ diff --git a/test/font-face-get-type.c b/test/font-face-get-type.c new file mode 100644 index 000000000..c21d5c238 --- /dev/null +++ b/test/font-face-get-type.c @@ -0,0 +1,64 @@ +/* + * Copyright © 2006 Red Hat, Inc. + * + * 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 + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. 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: Carl D. Worth + */ + +#include "cairo-test.h" + +int +main (void) +{ + cairo_surface_t *surface; + cairo_t *cr; + cairo_font_face_t *font_face; + + cairo_test_init ("font-face-get-type"); + + cairo_test_log ("Creating cairo context and obtaining a font face\n"); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + cr = cairo_create (surface); + + cairo_select_font_face (cr, "Bitstream Vera Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + + font_face = cairo_get_font_face (cr); + + cairo_test_log ("Testing return value of cairo_font_face_get_type\n"); + + if (cairo_font_face_get_type (font_face) != CAIRO_FONT_TYPE_TOY) { + cairo_test_log ("Unexpected value %d from cairo_font_face_get_type (expected %d)\n", + cairo_font_face_get_type (font_face), CAIRO_FONT_TYPE_TOY); + return CAIRO_TEST_FAILURE; + } + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + return CAIRO_TEST_SUCCESS; +} + + + + diff --git a/test/ft-font-create-for-ft-face.c b/test/ft-font-create-for-ft-face.c index bf668b9d8..0bb3b8c0d 100644 --- a/test/ft-font-create-for-ft-face.c +++ b/test/ft-font-create-for-ft-face.c @@ -64,6 +64,13 @@ draw (cairo_t *cr, int width, int height) font_face = cairo_ft_font_face_create_for_pattern (resolved); + if (cairo_font_face_get_type (font_face) != CAIRO_FONT_TYPE_FT) { + cairo_test_log ("Unexpected value from cairo_font_face_get_type: %d (expected %d)\n", + cairo_font_face_get_type (font_face), CAIRO_FONT_TYPE_FT); + cairo_font_face_destroy (font_face); + return CAIRO_TEST_FAILURE; + } + cairo_matrix_init_identity (&font_matrix); cairo_get_matrix (cr, &ctm); @@ -82,6 +89,13 @@ draw (cairo_t *cr, int width, int height) FcPatternDestroy (pattern); FcPatternDestroy (resolved); + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_FT) { + cairo_test_log ("Unexpected value from cairo_scaled_font_get_type: %d (expected %d)\n", + cairo_scaled_font_get_type (scaled_font), CAIRO_FONT_TYPE_FT); + cairo_scaled_font_destroy (scaled_font); + return CAIRO_TEST_FAILURE; + } + if (!ft_face) { cairo_test_log ("Failed to get an ft_face with cairo_ft_scaled_font_lock_face\n"); cairo_scaled_font_destroy (scaled_font); diff --git a/test/leaky-polygon-ps-argb32-ref.png b/test/leaky-polygon-ps-argb32-ref.png new file mode 100644 index 000000000..23af504ec Binary files /dev/null and b/test/leaky-polygon-ps-argb32-ref.png differ diff --git a/test/line-width-ps-argb32-ref.png b/test/line-width-ps-argb32-ref.png new file mode 100644 index 000000000..ef6abb74e Binary files /dev/null and b/test/line-width-ps-argb32-ref.png differ diff --git a/test/mask.c b/test/mask.c index a9414e046..6d0e45878 100644 --- a/test/mask.c +++ b/test/mask.c @@ -213,12 +213,10 @@ draw (cairo_t *cr, int width, int height) int x = i * (WIDTH + PAD) + PAD; int y = (ARRAY_SIZE (mask_funcs) * k + j) * (HEIGHT + PAD) + PAD; - /* Clear area we are going to be drawing onto */ + /* Clear intermediate surface we are going to be drawing onto */ cairo_save (cr2); - cairo_set_source_rgba (cr2, 0, 0, 0, 0); /* transparent */ - cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE); - cairo_rectangle (cr2, x, y, WIDTH, HEIGHT); - cairo_fill (cr2); + cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR); + cairo_paint (cr2); cairo_restore (cr2); /* draw */ diff --git a/test/path-data-ps-argb32-ref.png b/test/path-data-ps-argb32-ref.png new file mode 100644 index 000000000..b724a0f89 Binary files /dev/null and b/test/path-data-ps-argb32-ref.png differ diff --git a/test/pattern-get-type.c b/test/pattern-get-type.c new file mode 100644 index 000000000..a6aba03aa --- /dev/null +++ b/test/pattern-get-type.c @@ -0,0 +1,74 @@ +/* + * Copyright © 2006 Red Hat, Inc. + * + * 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 + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. 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: Carl D. Worth + */ + +#include "cairo-test.h" + +int +main (void) +{ + cairo_surface_t *surface; + cairo_pattern_t *solid_rgb, *solid_rgba, *surface_pattern, *linear, *radial; + + cairo_test_init ("pattern-get-type"); + + cairo_test_log ("Creating patterns of all types\n"); + + solid_rgb = cairo_pattern_create_rgb (0.0, 0.1, 0.2); + solid_rgba = cairo_pattern_create_rgba (0.3, 0.4, 0.5, 0.6); + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + 1, 1); + surface_pattern = cairo_pattern_create_for_surface (surface); + linear = cairo_pattern_create_linear (0.0, 0.0, 10.0, 10.0); + radial = cairo_pattern_create_radial (10.0, 10.0, 0.1, + 10.0, 10.0, 1.0); + + cairo_test_log ("Verifying return values of cairo_pattern_get_type\n"); + + if (cairo_pattern_get_type (solid_rgb) != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_TEST_FAILURE; + + if (cairo_pattern_get_type (solid_rgba) != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_TEST_FAILURE; + + if (cairo_pattern_get_type (surface_pattern) != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_TEST_FAILURE; + + if (cairo_pattern_get_type (linear) != CAIRO_PATTERN_TYPE_LINEAR) + return CAIRO_TEST_FAILURE; + + if (cairo_pattern_get_type (radial) != CAIRO_PATTERN_TYPE_RADIAL) + return CAIRO_TEST_FAILURE; + + cairo_test_log ("Cleaning up\n"); + + cairo_pattern_destroy (solid_rgb); + cairo_pattern_destroy (solid_rgba); + cairo_pattern_destroy (surface_pattern); + cairo_surface_destroy (surface); + cairo_pattern_destroy (linear); + cairo_pattern_destroy (radial); + + return CAIRO_TEST_SUCCESS; +} diff --git a/test/rectangle-rounding-error-ps-argb32-ref.png b/test/rectangle-rounding-error-ps-argb32-ref.png new file mode 100644 index 000000000..2753f6d24 Binary files /dev/null and b/test/rectangle-rounding-error-ps-argb32-ref.png differ diff --git a/test/show-glyphs-many-ref.png b/test/show-glyphs-many-ref.png new file mode 100644 index 000000000..b61c5f7b6 Binary files /dev/null and b/test/show-glyphs-many-ref.png differ diff --git a/test/show-glyphs-many-rgb24-ref.png b/test/show-glyphs-many-rgb24-ref.png new file mode 100644 index 000000000..450e8e0bb Binary files /dev/null and b/test/show-glyphs-many-rgb24-ref.png differ diff --git a/test/show-glyphs-many.c b/test/show-glyphs-many.c new file mode 100644 index 000000000..3c444114d --- /dev/null +++ b/test/show-glyphs-many.c @@ -0,0 +1,127 @@ +/* + * Copyright © 2006 Red Hat, Inc. + * + * 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 + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. 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: Carl D. Worth + */ + +#include "cairo-test.h" + +#include + +/* Bug history + * + * 2006-01-07 Jon Hellan + * + * Jon opened the following bug report: + * + * _XError from XRenderCompositeText8 + * https://bugs.freedesktop.org/show_bug.cgi?id=5528 + * + * 2006-03-02 Carl Worth + * + * I wrote this test case to demonstrate the bug. + * + * Approach: + * + * Draw 65535 glyphs white-on-white all on top of each other. + * + * Rationale: + * + * The number 65535 comes from the original bug report. + * + * I would use cairo_show_text with a long string of 'x's say, + * but then the surface would need to be enormous to contain + * them. A smaller surface could be used, but I fear that at some + * point the off-surface glyph drawing would be optimized away + * and not exercise the bug. + * + * So, to keep the surface size under control, I use + * cairo_show_glyphs which allows me to place the glyphs all on + * top of each other. But, since cairo doesn't provide any + * character-to-glyphs mapping, I can't get a reliable glyph + * index (for character 'x' for example). So I just "guess" a + * glyph index and use white-on-white drawing to ignore the + * result. (I don't care what's drawn---I just want to ensure + * that things don't crash.) + * + * Status: I replicated bug. The largest value of NUM_GLYPHS for + * which I saw success is 21842. + */ + +#define TEXT_SIZE 12 +#define NUM_GLYPHS 65535 + +/* This is the index into the font for what glyph we'll draw. Since we + * don't guarantee we'll get any particular font, we can't relibably + * get any particular glyph. But we don't care what we draw anyway, + * (see discussion of white-on-white drawing above). For what it's + * worth, this appears to be giving me 'M' with Bitstream Vera + * Sans Mono. */ +#define GLYPH_INDEX 48 + +cairo_test_t test = { + "show-glyphs-many", + "Test that cairo_show_glyps works when handed 'many' glyphs", + 9, 11 +}; + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_glyph_t glyphs[NUM_GLYPHS]; + cairo_font_options_t *font_options; + int i; + + /* Initialize our giant array of glyphs. */ + for (i=0; i < NUM_GLYPHS; i++) { + glyphs[i].index = GLYPH_INDEX; + glyphs[i].x = 1.0; + glyphs[i].y = height - 1; + } + + /* Paint white background. */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */ + cairo_paint (cr); + + cairo_select_font_face (cr, "Bitstream Vera Sans Mono", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (cr, TEXT_SIZE); + + font_options = cairo_font_options_create (); + + cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY); + + cairo_set_font_options (cr, font_options); + cairo_font_options_destroy (font_options); + + cairo_show_glyphs (cr, glyphs, NUM_GLYPHS); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test, draw); +} diff --git a/test/show-text-current-point-ps-argb32-ref.png b/test/show-text-current-point-ps-argb32-ref.png new file mode 100644 index 000000000..f00681b9d Binary files /dev/null and b/test/show-text-current-point-ps-argb32-ref.png differ diff --git a/test/show-text-current-point-svg-argb32-ref.png b/test/show-text-current-point-svg-argb32-ref.png new file mode 100644 index 000000000..717ad7e42 Binary files /dev/null and b/test/show-text-current-point-svg-argb32-ref.png differ diff --git a/test/show-text-current-point-svg-rgb24-ref.png b/test/show-text-current-point-svg-rgb24-ref.png new file mode 100644 index 000000000..717ad7e42 Binary files /dev/null and b/test/show-text-current-point-svg-rgb24-ref.png differ diff --git a/test/text-antialias-gray-ps-argb32-ref.png b/test/text-antialias-gray-ps-argb32-ref.png new file mode 100644 index 000000000..acb273cb7 Binary files /dev/null and b/test/text-antialias-gray-ps-argb32-ref.png differ diff --git a/test/text-antialias-gray-svg-argb32-ref.png b/test/text-antialias-gray-svg-argb32-ref.png new file mode 100644 index 000000000..aa64fbbec Binary files /dev/null and b/test/text-antialias-gray-svg-argb32-ref.png differ diff --git a/test/text-antialias-gray-svg-rgb24-ref.png b/test/text-antialias-gray-svg-rgb24-ref.png new file mode 100644 index 000000000..aa64fbbec Binary files /dev/null and b/test/text-antialias-gray-svg-rgb24-ref.png differ diff --git a/test/text-antialias-none-ps-argb32-ref.png b/test/text-antialias-none-ps-argb32-ref.png new file mode 100644 index 000000000..acb273cb7 Binary files /dev/null and b/test/text-antialias-none-ps-argb32-ref.png differ diff --git a/test/text-antialias-none-svg-argb32-ref.png b/test/text-antialias-none-svg-argb32-ref.png new file mode 100644 index 000000000..aa64fbbec Binary files /dev/null and b/test/text-antialias-none-svg-argb32-ref.png differ diff --git a/test/text-antialias-none-svg-rgb24-ref.png b/test/text-antialias-none-svg-rgb24-ref.png new file mode 100644 index 000000000..aa64fbbec Binary files /dev/null and b/test/text-antialias-none-svg-rgb24-ref.png differ diff --git a/test/text-antialias-subpixel-ps-argb32-ref.png b/test/text-antialias-subpixel-ps-argb32-ref.png new file mode 100644 index 000000000..acb273cb7 Binary files /dev/null and b/test/text-antialias-subpixel-ps-argb32-ref.png differ diff --git a/test/text-antialias-subpixel-svg-argb32-ref.png b/test/text-antialias-subpixel-svg-argb32-ref.png new file mode 100644 index 000000000..aa64fbbec Binary files /dev/null and b/test/text-antialias-subpixel-svg-argb32-ref.png differ diff --git a/test/text-antialias-subpixel-svg-rgb24-ref.png b/test/text-antialias-subpixel-svg-rgb24-ref.png new file mode 100644 index 000000000..aa64fbbec Binary files /dev/null and b/test/text-antialias-subpixel-svg-rgb24-ref.png differ diff --git a/test/text-pattern-svg-argb32-ref.png b/test/text-pattern-svg-argb32-ref.png new file mode 100644 index 000000000..e4b5f753e Binary files /dev/null and b/test/text-pattern-svg-argb32-ref.png differ diff --git a/test/text-pattern-svg-rgb24-ref.png b/test/text-pattern-svg-rgb24-ref.png new file mode 100644 index 000000000..6f3510cbf Binary files /dev/null and b/test/text-pattern-svg-rgb24-ref.png differ diff --git a/test/transforms-ps-argb32-ref.png b/test/transforms-ps-argb32-ref.png new file mode 100644 index 000000000..4858364f6 Binary files /dev/null and b/test/transforms-ps-argb32-ref.png differ diff --git a/test/unantialiased-shapes-ps-argb32-ref.png b/test/unantialiased-shapes-ps-argb32-ref.png new file mode 100644 index 000000000..dc64e3616 Binary files /dev/null and b/test/unantialiased-shapes-ps-argb32-ref.png differ diff --git a/test/unantialiased-shapes-svg-argb32-ref.png b/test/unantialiased-shapes-svg-argb32-ref.png new file mode 100644 index 000000000..da297735b Binary files /dev/null and b/test/unantialiased-shapes-svg-argb32-ref.png differ diff --git a/test/unantialiased-shapes-svg-rgb24-ref.png b/test/unantialiased-shapes-svg-rgb24-ref.png new file mode 100644 index 000000000..da297735b Binary files /dev/null and b/test/unantialiased-shapes-svg-rgb24-ref.png differ