diff options
author | Carl Worth <cworth@cworth.org> | 2006-04-18 15:18:31 -0700 |
---|---|---|
committer | Carl Worth <cworth@cworth.org> | 2006-04-18 15:18:31 -0700 |
commit | 448e904b2a64769aa9cfd8ea011c2b5fb6ec883b (patch) | |
tree | 3c6648d822914691133d97bc17b3ff0bd533c240 | |
parent | cb778760cb87e727a701603bcea3a2cdc063d785 (diff) | |
parent | ffab2592fc5d0ccd498aff2f4e645eefe351b61b (diff) | |
download | cairo-448e904b2a64769aa9cfd8ea011c2b5fb6ec883b.tar.gz |
Merge branch 'cairo' into new-sub-path
Conflicts:
src/cairo-path-stroke.c
src/cairo-pdf-surface.c
src/cairo-ps-surface.c
117 files changed, 4942 insertions, 1528 deletions
diff --git a/.gitignore b/.gitignore index 73e1e4e75..f8e50fe9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +ChangeLog Makefile Makefile.in aclocal.m4 @@ -14,6 +14,7 @@ John Ehresman <jpe@wingide.com> Build fixes for win32 John Ellson <ellson@research.att.com> First font/glyph extents functions Behdad Esfahbod <behdad@behdad.org> Release script improvements, bug fixes. Bertram Felgenhauer <int-e@gmx.de> Fixes for subtle arithmetic errors +Bdale Garbee <bdale@gag.com> Provided essential support for cairo achitecture sessions J. Ali Harlow <ali@avrc.city.ac.uk> win32 backend updates Richard Henderson <rth@twiddle.net> "slim" macros for better shared libraries James Henstridge <james@daa.com.au> Build fixes related to freetype @@ -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 @@ -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 @@ <xi:include href="xml/cairo-ps.xml"/> <xi:include href="xml/cairo-win32.xml"/> <xi:include href="xml/cairo-beos.xml"/> + <xi:include href="xml/cairo-svg.xml"/> <!-- Experimental Quartz and XCB backends removed from the public doc for now. <xi:include href="xml/cairo-quartz.xml"/> diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index bf84dd5ec..59f509805 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -109,6 +109,14 @@ cairo_xlib_surface_create_with_xrender_format </SECTION> <SECTION> +<FILE>cairo-svg</FILE> +<TITLE>SVG Surfaces</TITLE> +cairo_svg_surface_create +cairo_svg_surface_create_for_stream +cairo_svg_surface_set_dpi +</SECTION> + +<SECTION> <FILE>cairo-surface</FILE> <TITLE>cairo_surface_t</TITLE> cairo_surface_t diff --git a/doc/public/tmpl/cairo-svg.sgml b/doc/public/tmpl/cairo-svg.sgml new file mode 100644 index 000000000..31bf69257 --- /dev/null +++ b/doc/public/tmpl/cairo-svg.sgml @@ -0,0 +1,52 @@ +<!-- ##### SECTION Title ##### --> +SVG Surfaces + +<!-- ##### SECTION Short_Description ##### --> +Rendering SVG documents + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### SECTION Stability_Level ##### --> + + +<!-- ##### FUNCTION cairo_svg_surface_create ##### --> +<para> + +</para> + +@filename: +@width_in_points: +@height_in_points: +@Returns: + + +<!-- ##### FUNCTION cairo_svg_surface_create_for_stream ##### --> +<para> + +</para> + +@write_func: +@closure: +@width_in_points: +@height_in_points: +@Returns: + + +<!-- ##### FUNCTION cairo_svg_surface_set_dpi ##### --> +<para> + +</para> + +@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 <math.h> -// #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 <sys/int_types.h> -#elif defined (__OpenBSD__) || defined (_AIX) +#elif defined (__OpenBSD__) || defined (_AIX) || defined (__osf__) # include <inttypes.h> #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 <keithp@keithp.com> + */ + +#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 <keithp@keithp.com> + */ + +#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_rectangle_t rect = {dest_x, dest_y, width, height}; - _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); + cairo_bool_t can_draw_directly; + cairo_rectangle_t rect; + + /* 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; + + if (!can_draw_directly) { + rect.x = dest_x; + rect.y = dest_y; + rect.width = width; + rect.height = height; + + _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 <krh@redhat.com> + */ + +#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<cairo_beos_surface_t*>( + 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<cairo_beos_surface_t*>( + 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<cairo_beos_surface_t*>( 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<cairo_beos_surface_t*>( 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<cairo_surface_pattern_t*>(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<cairo_image_surface_t*>(src_surface); - BBitmap* bmp = _cairo_image_surface_to_bitmap(img_surface); - surface->view->PushState(); + 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<cairo_beos_surface_t*>(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; + } + } - // 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 }; + if (!bmp) + return CAIRO_INT_STATUS_UNSUPPORTED; - surface->view->SetDrawingMode(mode); - surface->view->SetHighColor(black); - surface->view->FillRect(dstRect); - } + // 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; + } - if (mode == B_OP_ALPHA && img_surface->format != CAIRO_FORMAT_ARGB32) { - mode = B_OP_COPY; + 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); + 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->DrawBitmap(bmp, srcRect, dstRect); - surface->view->PopState(); - delete bmp; + surface->view->PopState(); - return CAIRO_INT_STATUS_SUCCESS; - } + if (free_bmp) + delete bmp; - return CAIRO_INT_STATUS_UNSUPPORTED; + 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<cairo_beos_surface_t*>( @@ -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<cairo_beos_surface_t*>( + malloc(sizeof(cairo_beos_surface_t))); + if (surface == NULL) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return const_cast<cairo_surface_t*>(&_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<cairo_beos_surface_t*>( - malloc(sizeof(cairo_beos_surface_t))); - if (surface == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return const_cast<cairo_surface_t*>(&_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 @@ -131,6 +131,18 @@ cairo_font_face_destroy (cairo_font_face_t *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 <cworth@cworth.org> + */ + +#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<<LZW_BITS_MAX) + +#define LZW_SYMBOL_TABLE_SIZE 9013 +#define LZW_SYMBOL_MOD1 LZW_SYMBOL_TABLE_SIZE +#define LZW_SYMBOL_MOD2 9011 + +typedef struct _lzw_symbol_table { + lzw_symbol_t table[LZW_SYMBOL_TABLE_SIZE]; +} lzw_symbol_table_t; + +/* Initialize the hash table to entirely empty */ +static void +_lzw_symbol_table_init (lzw_symbol_table_t *table) +{ + memset (table->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 <cworth@cworth.org> + * Keith Packard <keithp@keithp.com> + */ + +#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_destroy (cairo_output_stream_t *stream) +_cairo_output_stream_close (cairo_output_stream_t *stream) { - if (stream->owns_closure_is_file) { - FILE *file = stream->closure; - fflush (file); - fclose (file); + 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 == 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); - stream->position += length; + if (stream->status) + return; - return stream->status; + stream->status = stream->write_func (stream->closure, data, length); + stream->position += length; } 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, 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 (fwrite (data, 1, length, fp) == length) + 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 _cairo_output_stream_create (stdio_write, stdio_flush, file); +} - return stream; +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_<foo>_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 <cworth@cworth.org> + * Keith Packard <keithp@keithp.com> */ /* 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); + + 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_meta_surface_replay (surface->meta, image); - pattern = cairo_pattern_create_for_surface (image); + pattern = cairo_pattern_create_for_surface (image); - _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, pattern); + _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, pattern); - cairo_pattern_destroy (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; - cairo_surface_destroy (image); + 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 @@ -613,6 +616,16 @@ _cairo_stroker_move_to (void *closure, cairo_point_t *point) } 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) { cairo_status_t status; @@ -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); @@ -525,6 +525,18 @@ cairo_pattern_reference (cairo_pattern_t *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); } @@ -1321,55 +1333,6 @@ _cairo_pdf_path_close_path (void *closure) } 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, void *abstract_dst, @@ -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 <time.h> @@ -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,20 +96,73 @@ 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 cairo_status_t +_cairo_ps_surface_path_move_to (void *closure, cairo_point_t *point) +{ + cairo_output_stream_t *output_stream = closure; + + _cairo_output_stream_printf (output_stream, + "%f %f moveto ", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_path_line_to (void *closure, cairo_point_t *point) +{ + cairo_output_stream_t *output_stream = closure; + + _cairo_output_stream_printf (output_stream, + "%f %f lineto ", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_path_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) +{ + cairo_output_stream_t *output_stream = closure; + + _cairo_output_stream_printf (output_stream, + "%f %f %f %f %f %f curveto ", + _cairo_fixed_to_double (b->x), + _cairo_fixed_to_double (b->y), + _cairo_fixed_to_double (c->x), + _cairo_fixed_to_double (c->y), + _cairo_fixed_to_double (d->x), + _cairo_fixed_to_double (d->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_path_close_path (void *closure) +{ + cairo_output_stream_t *output_stream = closure; + + _cairo_output_stream_printf (output_stream, + "closepath\n"); + + return CAIRO_STATUS_SUCCESS; +} static void _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) @@ -90,34 +171,341 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) now = time (NULL); - _cairo_output_stream_printf (surface->stream, + _cairo_output_stream_printf (surface->final_stream, "%%!PS-Adobe-3.0\n" "%%%%Creator: cairo (http://cairographics.org)\n" "%%%%CreationDate: %s" - "%%%%Pages: (atend)\n" + "%%%%Pages: %d\n" "%%%%BoundingBox: %f %f %f %f\n", ctime (&now), + surface->num_pages, 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" + _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->stream, + _cairo_output_stream_printf (surface->final_stream, "%%%%Trailer\n" - "%%%%Pages: %d\n" - "%%%%EOF\n", - surface->num_pages); + "%%%%EOF\n"); } static cairo_surface_t * @@ -125,6 +513,7 @@ _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)); @@ -135,29 +524,40 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, _cairo_surface_init (&surface->base, &cairo_ps_surface_backend); - surface->stream = stream; + 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; -#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->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; - 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); + width, height, + &cairo_ps_surface_paginated_backend); } /** @@ -182,11 +582,13 @@ 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_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; } @@ -220,11 +622,13 @@ cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, double width_in_points, double height_in_points) { + cairo_status_t status; cairo_output_stream_t *stream; - stream = _cairo_output_stream_create (write_func, closure); - if (stream == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); + 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; } @@ -276,44 +680,156 @@ cairo_ps_surface_set_dpi (cairo_surface_t *surface, 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 */ +/* 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; -#if DONE_ADDING_FONTS_SUPPORT_BACK_AFTER_SWITCHING_TO_PAGINATED - _cairo_ps_surface_write_font_subsets (surface); -#endif + /* Save final_stream to be restored later. */ + final_stream = surface->final_stream; - _cairo_ps_surface_emit_footer (surface); + 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); -#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_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); - return CAIRO_STATUS_SUCCESS; + 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 void -_cairo_ps_surface_start_page (cairo_ps_surface_t *surface) +static cairo_int_status_t +_cairo_ps_surface_start_page (void *abstract_surface) { - _cairo_output_stream_printf (surface->stream, - "%%%%Page: %d\n", - ++surface->num_pages); + 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", @@ -321,7 +837,7 @@ _cairo_ps_surface_start_page (cairo_ps_surface_t *surface) 1.0/surface->base.device_x_scale, -1.0/surface->base.device_y_scale); - surface->need_start_page = FALSE; + return _cairo_output_stream_get_status (surface->stream); } static void @@ -329,8 +845,6 @@ _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 @@ -354,132 +868,6 @@ _cairo_ps_surface_show_page (void *abstract_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; } @@ -493,192 +881,174 @@ color_is_gray (cairo_color_t *color) } 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) +surface_pattern_supported (const cairo_surface_pattern_t *pattern) { - switch (format) { - case CAIRO_FORMAT_ARGB32: - return TRUE; - case CAIRO_FORMAT_RGB24: - return FALSE; - case CAIRO_FORMAT_A8: - return TRUE; - case CAIRO_FORMAT_A1: + if (pattern->surface->backend->acquire_source_image != NULL) 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) +pattern_supported (const cairo_pattern_t *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; + 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_bool_t -operator_always_opaque (cairo_operator_t op) +static cairo_int_status_t +operation_supported (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) { - 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: + if (! pattern_supported (pattern)) return FALSE; - case CAIRO_OPERATOR_DEST: + if (_cairo_operator_always_opaque (op)) 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: + if (_cairo_operator_always_translucent (op)) return FALSE; - } - return FALSE; + + return _cairo_pattern_is_opaque (pattern); } -static cairo_bool_t -operator_always_translucent (cairo_operator_t op) +static cairo_int_status_t +_analyze_operation (cairo_ps_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) { - switch (op) { - case CAIRO_OPERATOR_CLEAR: + if (operation_supported (surface, op, pattern)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_UNSUPPORTED; +} - case CAIRO_OPERATOR_SOURCE: - return FALSE; - - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - return FALSE; +/* 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 - 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; +typedef struct _string_array_stream { + cairo_output_stream_t *output; + int column; + int string_size; +} string_array_stream_t; - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - return TRUE; +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 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); + return _cairo_output_stream_get_status (stream->output); } -static cairo_bool_t -pattern_type_supported (const cairo_pattern_t *pattern) +static cairo_status_t +_string_array_stream_close (void *closure) { - if (pattern->type == CAIRO_PATTERN_SOLID) - return TRUE; - return FALSE; -} + cairo_status_t status; + string_array_stream_t *stream = closure; -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); -} + _cairo_output_stream_printf (stream->output, ")\n"); -/* PS Output - this section handles output of the parts of the meta - * surface we can render natively in PS. */ + status = _cairo_output_stream_get_status (stream->output); + + free (stream); -static void * -compress_dup (const void *data, unsigned long data_size, - unsigned long *compressed_size) + 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) { - void *compressed; + string_array_stream_t *stream; - /* 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; + stream = malloc (sizeof (string_array_stream_t)); + if (stream == NULL) + return (cairo_output_stream_t *) &cairo_output_stream_nil; - compress (compressed, compressed_size, data, data_size); + stream->output = output; + stream->column = 0; + stream->string_size = 0; - return compressed; + 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) + cairo_matrix_t *matrix, + char *name) { cairo_status_t status; unsigned char *rgb, *compressed; @@ -688,6 +1058,7 @@ emit_image (cairo_ps_surface_t *surface, 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. */ @@ -745,12 +1116,33 @@ emit_image (cairo_ps_surface_t *surface, } } - compressed = compress_dup (rgb, rgb_size, &compressed_size); + /* 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. */ @@ -758,30 +1150,33 @@ emit_image (cairo_ps_surface_t *surface, cairo_matrix_multiply (&d2i, &d2i, matrix); _cairo_output_stream_printf (surface->stream, - "/DeviceRGB setcolorspace\n" - "<<\n" + "/%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 currentfile\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", + " >>\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); - /* 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); @@ -798,11 +1193,11 @@ emit_solid_pattern (cairo_ps_surface_t *surface, { if (color_is_gray (&pattern->color)) _cairo_output_stream_printf (surface->stream, - "%f setgray\n", + "%f G\n", pattern->color.red); else _cairo_output_stream_printf (surface->stream, - "%f %f %f setrgbcolor\n", + "%f %f %f R\n", pattern->color.red, pattern->color.green, pattern->color.blue); @@ -812,7 +1207,41 @@ static void emit_surface_pattern (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern) { - /* XXX: NYI */ + 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 @@ -837,276 +1266,24 @@ emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern) * different pattern. */ switch (pattern->type) { - case CAIRO_PATTERN_SOLID: + case CAIRO_PATTERN_TYPE_SOLID: emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); break; - case CAIRO_PATTERN_SURFACE: + case CAIRO_PATTERN_TYPE_SURFACE: emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); break; - case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_TYPE_LINEAR: emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); break; - case CAIRO_PATTERN_RADIAL: + case CAIRO_PATTERN_TYPE_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) -{ - cairo_output_stream_t *output_stream = closure; - - _cairo_output_stream_printf (output_stream, - "%f %f moveto ", - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_path_line_to (void *closure, cairo_point_t *point) -{ - cairo_output_stream_t *output_stream = closure; - - _cairo_output_stream_printf (output_stream, - "%f %f lineto ", - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_path_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) -{ - cairo_output_stream_t *output_stream = closure; - - _cairo_output_stream_printf (output_stream, - "%f %f %f %f %f %f curveto ", - _cairo_fixed_to_double (b->x), - _cairo_fixed_to_double (b->y), - _cairo_fixed_to_double (c->x), - _cairo_fixed_to_double (c->y), - _cairo_fixed_to_double (d->x), - _cairo_fixed_to_double (d->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ps_surface_path_close_path (void *closure) -{ - cairo_output_stream_t *output_stream = closure; - - _cairo_output_stream_printf (output_stream, - "closepath\n"); - - return CAIRO_STATUS_SUCCESS; -} - 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->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_paint\n"); - if (surface->need_start_page) - _cairo_ps_surface_start_page (surface); + emit_pattern (surface, source); - /* 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; + _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; +} - if (surface->fallback) - return CAIRO_STATUS_SUCCESS; +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; + } +} - if (pattern_operation_needs_fallback (op, pattern)) - return _cairo_ps_surface_add_fallback_area (surface, dest_x, dest_y, width, height); +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; + } +} - _cairo_output_stream_printf (stream, - "%% _cairo_ps_surface_old_show_glyphs\n"); +static cairo_int_status_t +_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; - emit_pattern (surface, pattern); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _analyze_operation (surface, op, source); - /* FIXME: Need to optimize this so we only do this sequence if the - * font isn't already set. */ + assert (operation_supported (surface, op, source)); + + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_stroke\n"); + + emit_pattern (surface, source); - 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. */ + "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); - 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); - + /* + * 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); } - - return CAIRO_STATUS_SUCCESS; + /* 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; } -#endif static cairo_int_status_t -_cairo_ps_surface_fill (void *abstract_surface, +_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; - - // 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); - - *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; - } + cairo_quartz_surface_t *surface = abstract_surface; - *image_out = surface->image; - *image_extra = NULL; + if (CGBitmapContextGetBitmapInfo (surface->context) != 0) { + /* XXX: We can create an image out of the bitmap here */ + } - 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; + + x1 = surface->extents.x; + x2 = surface->extents.x + surface->extents.width; + y1 = surface->extents.y; + y2 = surface->extents.y + surface->extents.height; + + 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 = 0; - image_rect->y = 0; - image_rect->width = surface->image->width; - image_rect->height = surface->image->height; + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; - *image_out = surface->image; - if (image_extra) - *image_extra = NULL; + 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(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); + rect = CGRectMake (image_rect->x, image_rect->y, image_rect->width, image_rect->height); - if (surface->flipped) - CGContextRestoreGState (surface->context); + if (surface->y_grows_down) { + CGContextSaveGState (surface->context); + CGContextTranslateCTM (surface->context, 0, image_rect->height + 2 * image_rect->y); + CGContextScaleCTM (surface->context, 1, -1); + } - memset(surface->image->data, 0, surface->width * surface->height * 4); + 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 (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); + if (region) { + surface->clip_region = pixman_region_create (); + pixman_region_copy (surface->clip_region, region); + } else + surface->clip_region = NULL; + + 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; - - // 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); + surface->clip_region = NULL; + surface->y_grows_down = y_grows_down; + + 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)); @@ -119,6 +119,18 @@ _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, } /** + * 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 */ @@ -119,6 +122,22 @@ _cairo_surface_set_error (cairo_surface_t *surface, } /** + * 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,18 +317,46 @@ _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, cairo_matrix_t *matrix) @@ -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 <otaylor@redhat.com> + * Stuart Parmenter <stuart@mozilla.com> + * Vladimir Vukicevic <vladimir@pobox.com> */ #include <stdio.h> @@ -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 (surface->saved_clip) { - DeleteObject (surface->saved_clip); - surface->saved_clip = NULL; - } + if (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region (reset)"); - 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,8 +1029,19 @@ 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); @@ -1016,6 +1049,28 @@ cairo_win32_surface_create (HDC hdc) } /** + * 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 <X11/extensions/Xrender.h> +#include <X11/extensions/renderproto.h> /* 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; @@ -2027,6 +2033,46 @@ cairo_get_font_options (cairo_t *cr, } /** + * 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 * @utf8: a string of text, encoded in UTF-8 @@ -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 @@ -856,6 +856,10 @@ 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); cairo_public void @@ -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_<type>_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_<type>_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); @@ -1151,13 +1199,63 @@ 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_<type>_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_<type>_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; + } + + 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; - return _cairo_paginated_surface_create (target, content, width, height); + 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 Binary files differnew file mode 100644 index 000000000..d6742957a --- /dev/null +++ b/test/caps-joins-ps-argb32-ref.png diff --git a/test/caps-sub-paths-ps-argb32-ref.png b/test/caps-sub-paths-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..a7bc1acb4 --- /dev/null +++ b/test/caps-sub-paths-ps-argb32-ref.png diff --git a/test/clip-fill-rule-ps-argb32-ref.png b/test/clip-fill-rule-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..6d97e04de --- /dev/null +++ b/test/clip-fill-rule-ps-argb32-ref.png diff --git a/test/clip-nesting-ps-argb32-ref.png b/test/clip-nesting-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..1bd43d7a1 --- /dev/null +++ b/test/clip-nesting-ps-argb32-ref.png diff --git a/test/clip-twice-ps-argb32-ref.png b/test/clip-twice-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..9ec76b4ef --- /dev/null +++ b/test/clip-twice-ps-argb32-ref.png diff --git a/test/dash-caps-joins-ps-argb32-ref.png b/test/dash-caps-joins-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..629b97b34 --- /dev/null +++ b/test/dash-caps-joins-ps-argb32-ref.png diff --git a/test/dash-caps-joins-ref.png b/test/dash-caps-joins-ref.png Binary files differindex 5c887c5c9..9218ae499 100644 --- a/test/dash-caps-joins-ref.png +++ b/test/dash-caps-joins-ref.png diff --git a/test/dash-caps-joins-rgb24-ref.png b/test/dash-caps-joins-rgb24-ref.png Binary files differindex 28339f8ed..6ec274dfb 100644 --- a/test/dash-caps-joins-rgb24-ref.png +++ b/test/dash-caps-joins-rgb24-ref.png diff --git a/test/dash-offset-negative-ps-argb32-ref.png b/test/dash-offset-negative-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..518e1c925 --- /dev/null +++ b/test/dash-offset-negative-ps-argb32-ref.png diff --git a/test/dash-zero-length-ps-argb32-ref.png b/test/dash-zero-length-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..5c7fab579 --- /dev/null +++ b/test/dash-zero-length-ps-argb32-ref.png diff --git a/test/dash-zero-length-ref.png b/test/dash-zero-length-ref.png Binary files differnew file mode 100644 index 000000000..aca856f2b --- /dev/null +++ b/test/dash-zero-length-ref.png diff --git a/test/dash-zero-length-rgb24-ref.png b/test/dash-zero-length-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..f5e40e85c --- /dev/null +++ b/test/dash-zero-length-rgb24-ref.png 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 <jeff@infidigm.net> + */ + +#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 Binary files differnew file mode 100644 index 000000000..0df3205df --- /dev/null +++ b/test/fill-and-stroke-ps-argb32-ref.png diff --git a/test/fill-rule-ps-argb32-ref.png b/test/fill-rule-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..b5487e683 --- /dev/null +++ b/test/fill-rule-ps-argb32-ref.png 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 <cworth@cworth.org> + */ + +#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 Binary files differnew file mode 100644 index 000000000..23af504ec --- /dev/null +++ b/test/leaky-polygon-ps-argb32-ref.png diff --git a/test/line-width-ps-argb32-ref.png b/test/line-width-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..ef6abb74e --- /dev/null +++ b/test/line-width-ps-argb32-ref.png 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 Binary files differnew file mode 100644 index 000000000..b724a0f89 --- /dev/null +++ b/test/path-data-ps-argb32-ref.png 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 <cworth@cworth.org> + */ + +#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 Binary files differnew file mode 100644 index 000000000..2753f6d24 --- /dev/null +++ b/test/rectangle-rounding-error-ps-argb32-ref.png diff --git a/test/show-glyphs-many-ref.png b/test/show-glyphs-many-ref.png Binary files differnew file mode 100644 index 000000000..b61c5f7b6 --- /dev/null +++ b/test/show-glyphs-many-ref.png diff --git a/test/show-glyphs-many-rgb24-ref.png b/test/show-glyphs-many-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..450e8e0bb --- /dev/null +++ b/test/show-glyphs-many-rgb24-ref.png 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 <cworth@cworth.org> + */ + +#include "cairo-test.h" + +#include <string.h> + +/* Bug history + * + * 2006-01-07 Jon Hellan <hellan@acm.org> + * + * Jon opened the following bug report: + * + * _XError from XRenderCompositeText8 + * https://bugs.freedesktop.org/show_bug.cgi?id=5528 + * + * 2006-03-02 Carl Worth <cworth@cworth.org> + * + * 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 Binary files differnew file mode 100644 index 000000000..f00681b9d --- /dev/null +++ b/test/show-text-current-point-ps-argb32-ref.png diff --git a/test/show-text-current-point-svg-argb32-ref.png b/test/show-text-current-point-svg-argb32-ref.png Binary files differnew file mode 100644 index 000000000..717ad7e42 --- /dev/null +++ b/test/show-text-current-point-svg-argb32-ref.png diff --git a/test/show-text-current-point-svg-rgb24-ref.png b/test/show-text-current-point-svg-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..717ad7e42 --- /dev/null +++ b/test/show-text-current-point-svg-rgb24-ref.png diff --git a/test/text-antialias-gray-ps-argb32-ref.png b/test/text-antialias-gray-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..acb273cb7 --- /dev/null +++ b/test/text-antialias-gray-ps-argb32-ref.png diff --git a/test/text-antialias-gray-svg-argb32-ref.png b/test/text-antialias-gray-svg-argb32-ref.png Binary files differnew file mode 100644 index 000000000..aa64fbbec --- /dev/null +++ b/test/text-antialias-gray-svg-argb32-ref.png diff --git a/test/text-antialias-gray-svg-rgb24-ref.png b/test/text-antialias-gray-svg-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..aa64fbbec --- /dev/null +++ b/test/text-antialias-gray-svg-rgb24-ref.png diff --git a/test/text-antialias-none-ps-argb32-ref.png b/test/text-antialias-none-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..acb273cb7 --- /dev/null +++ b/test/text-antialias-none-ps-argb32-ref.png diff --git a/test/text-antialias-none-svg-argb32-ref.png b/test/text-antialias-none-svg-argb32-ref.png Binary files differnew file mode 100644 index 000000000..aa64fbbec --- /dev/null +++ b/test/text-antialias-none-svg-argb32-ref.png diff --git a/test/text-antialias-none-svg-rgb24-ref.png b/test/text-antialias-none-svg-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..aa64fbbec --- /dev/null +++ b/test/text-antialias-none-svg-rgb24-ref.png diff --git a/test/text-antialias-subpixel-ps-argb32-ref.png b/test/text-antialias-subpixel-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..acb273cb7 --- /dev/null +++ b/test/text-antialias-subpixel-ps-argb32-ref.png diff --git a/test/text-antialias-subpixel-svg-argb32-ref.png b/test/text-antialias-subpixel-svg-argb32-ref.png Binary files differnew file mode 100644 index 000000000..aa64fbbec --- /dev/null +++ b/test/text-antialias-subpixel-svg-argb32-ref.png diff --git a/test/text-antialias-subpixel-svg-rgb24-ref.png b/test/text-antialias-subpixel-svg-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..aa64fbbec --- /dev/null +++ b/test/text-antialias-subpixel-svg-rgb24-ref.png diff --git a/test/text-pattern-svg-argb32-ref.png b/test/text-pattern-svg-argb32-ref.png Binary files differnew file mode 100644 index 000000000..e4b5f753e --- /dev/null +++ b/test/text-pattern-svg-argb32-ref.png diff --git a/test/text-pattern-svg-rgb24-ref.png b/test/text-pattern-svg-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..6f3510cbf --- /dev/null +++ b/test/text-pattern-svg-rgb24-ref.png diff --git a/test/transforms-ps-argb32-ref.png b/test/transforms-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..4858364f6 --- /dev/null +++ b/test/transforms-ps-argb32-ref.png diff --git a/test/unantialiased-shapes-ps-argb32-ref.png b/test/unantialiased-shapes-ps-argb32-ref.png Binary files differnew file mode 100644 index 000000000..dc64e3616 --- /dev/null +++ b/test/unantialiased-shapes-ps-argb32-ref.png diff --git a/test/unantialiased-shapes-svg-argb32-ref.png b/test/unantialiased-shapes-svg-argb32-ref.png Binary files differnew file mode 100644 index 000000000..da297735b --- /dev/null +++ b/test/unantialiased-shapes-svg-argb32-ref.png diff --git a/test/unantialiased-shapes-svg-rgb24-ref.png b/test/unantialiased-shapes-svg-rgb24-ref.png Binary files differnew file mode 100644 index 000000000..da297735b --- /dev/null +++ b/test/unantialiased-shapes-svg-rgb24-ref.png |