diff options
author | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2009-09-15 16:33:27 +0200 |
---|---|---|
committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2009-09-15 16:33:27 +0200 |
commit | 0e79096a3831c10cf9735a7f518bfb65d0632923 (patch) | |
tree | 40e397ecf2131c041bd6e308d5c351a36be113f6 /src | |
parent | 23ad3f4fac1b4486013b7d4e7e0b8841625bfaaf (diff) |
Import upstream version 1.9.2
Diffstat (limited to 'src')
126 files changed, 19659 insertions, 7300 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index bf87efb..b017c17 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,18 +4,16 @@ include $(top_srcdir)/build/Makefile.am.common include $(srcdir)/Makefile.am.features EXTRA_DIST += Makefile.win32 Makefile.win32.features -MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features +#MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features AM_CPPFLAGS = -I$(srcdir) $(CAIRO_CFLAGS) +AM_LDFLAGS = $(CAIRO_LDFLAGS) if OS_WIN32 export_symbols = -export-symbols cairo.def cairo_def_dependency = cairo.def endif -EXTRA_DIST += cairo-supported-features.h -MAINTAINERCLEANFILES += cairo-supported-features.h - $(top_builddir)/config.h: $(top_srcdir)/config.h.in cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) config.h @@ -29,15 +27,17 @@ libcairo_la_SOURCES = \ $(enabled_cairo_private) \ $(enabled_cairo_sources) \ $(NULL) -libcairo_la_LDFLAGS = -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols) +libcairo_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols) libcairo_la_LIBADD = $(CAIRO_LIBS) libcairo_la_DEPENDENCIES = $(cairo_def_dependency) # Special headers cairoinclude_HEADERS += $(top_srcdir)/cairo-version.h +libcairo_la_SOURCES += cairo-version.h nodist_cairoinclude_HEADERS = cairo-features.h -libcairo_la_SOURCES += cairo-version.h cairo-features.h +nodist_libcairo_la_SOURCES = cairo-features.h BUILT_SOURCES += cairo-features.h cairo-supported-features.h +DISTCLEANFILES += cairo-features.h cairo-supported-features.h cairo-features.h cairo-supported-features.h: cd $(top_builddir) && ./config.status src/$@ @@ -84,9 +84,12 @@ TESTS += check-link$(EXEEXT) endif EXTRA_DIST += $(TESTS_SH) check-has-hidden-symbols.c -check_PROGRAMS += check-link +check_PROGRAMS += check-link check-skiplist check_link_LDADD = libcairo.la +check_skiplist_SOURCES = cairo-skiplist.c +check_skiplist_CPPFLAGS = -DMAIN $(AM_CPPFLAGS) + check: headers-standalone PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) diff --git a/src/Makefile.am.analysis b/src/Makefile.am.analysis index 4d527bf..ea9caec 100644 --- a/src/Makefile.am.analysis +++ b/src/Makefile.am.analysis @@ -23,7 +23,7 @@ uno: headers-standalone: $(enabled_cairo_headers) $(enabled_cairo_private) @echo Checking that enabled public/private headers can be compiled standalone @status=true; for f in $(enabled_cairo_headers) $(enabled_cairo_private); do \ - echo $(COMPILE) -o /dev/null $(srcdir)/$$f; \ + echo " CHECK $$f"; \ $(COMPILE) -o /dev/null $(srcdir)/$$f || status=false; \ done; $$status @touch $@ diff --git a/src/Makefile.am.features b/src/Makefile.am.features index 09dfbcf..69ac9e6 100644 --- a/src/Makefile.am.features +++ b/src/Makefile.am.features @@ -199,6 +199,20 @@ if CAIRO_HAS_DIRECTFB_SURFACE enabled_cairo_pkgconf += cairo-directfb.pc endif +unsupported_cairo_headers += $(cairo_script_headers) +all_cairo_headers += $(cairo_script_headers) +all_cairo_private += $(cairo_script_private) +all_cairo_sources += $(cairo_script_sources) +if CAIRO_HAS_SCRIPT_SURFACE +enabled_cairo_headers += $(cairo_script_headers) +enabled_cairo_private += $(cairo_script_private) +enabled_cairo_sources += $(cairo_script_sources) +endif +all_cairo_pkgconf += cairo-script.pc +if CAIRO_HAS_SCRIPT_SURFACE +enabled_cairo_pkgconf += cairo-script.pc +endif + supported_cairo_headers += $(cairo_ft_headers) all_cairo_headers += $(cairo_ft_headers) all_cairo_private += $(cairo_ft_private) @@ -213,6 +227,20 @@ if CAIRO_HAS_FT_FONT enabled_cairo_pkgconf += cairo-ft.pc endif +supported_cairo_headers += $(cairo_fc_headers) +all_cairo_headers += $(cairo_fc_headers) +all_cairo_private += $(cairo_fc_private) +all_cairo_sources += $(cairo_fc_sources) +if CAIRO_HAS_FC_FONT +enabled_cairo_headers += $(cairo_fc_headers) +enabled_cairo_private += $(cairo_fc_private) +enabled_cairo_sources += $(cairo_fc_sources) +endif +all_cairo_pkgconf += cairo-fc.pc +if CAIRO_HAS_FC_FONT +enabled_cairo_pkgconf += cairo-fc.pc +endif + supported_cairo_headers += $(cairo_ps_headers) all_cairo_headers += $(cairo_ps_headers) all_cairo_private += $(cairo_ps_private) diff --git a/src/Makefile.in b/src/Makefile.in index 66760a1..5f35173 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -96,8 +96,8 @@ DIST_COMMON = README $(am__cairoinclude_HEADERS_DIST) \ $(top_srcdir)/build/Makefile.am.common \ $(top_srcdir)/src/Makefile.sources EXTRA_PROGRAMS = -TESTS = $(am__EXEEXT_1) $(am__append_71) -check_PROGRAMS = check-link$(EXEEXT) +TESTS = $(am__EXEEXT_1) $(am__append_79) +check_PROGRAMS = check-link$(EXEEXT) check-skiplist$(EXEEXT) @CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_1 = $(cairo_xlib_headers) @CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_2 = $(cairo_xlib_private) @CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_3 = $(cairo_xlib_sources) @@ -150,25 +150,33 @@ check_PROGRAMS = check-link$(EXEEXT) @CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_50 = $(cairo_directfb_private) @CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_51 = $(cairo_directfb_sources) @CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_52 = cairo-directfb.pc -@CAIRO_HAS_FT_FONT_TRUE@am__append_53 = $(cairo_ft_headers) -@CAIRO_HAS_FT_FONT_TRUE@am__append_54 = $(cairo_ft_private) -@CAIRO_HAS_FT_FONT_TRUE@am__append_55 = $(cairo_ft_sources) -@CAIRO_HAS_FT_FONT_TRUE@am__append_56 = cairo-ft.pc -@CAIRO_HAS_PS_SURFACE_TRUE@am__append_57 = $(cairo_ps_headers) -@CAIRO_HAS_PS_SURFACE_TRUE@am__append_58 = $(cairo_ps_private) -@CAIRO_HAS_PS_SURFACE_TRUE@am__append_59 = $(cairo_ps_sources) -@CAIRO_HAS_PS_SURFACE_TRUE@am__append_60 = cairo-ps.pc -@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_61 = $(cairo_pdf_headers) -@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_62 = $(cairo_pdf_private) -@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_63 = $(cairo_pdf_sources) -@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_64 = cairo-pdf.pc -@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_65 = $(cairo_svg_headers) -@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_66 = $(cairo_svg_private) -@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_67 = $(cairo_svg_sources) -@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_68 = cairo-svg.pc -@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_69 = $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) -@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_70 = $(cairo_test_surfaces_sources) -@CROSS_COMPILING_FALSE@am__append_71 = check-link$(EXEEXT) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_53 = $(cairo_script_headers) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_54 = $(cairo_script_private) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_55 = $(cairo_script_sources) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_56 = cairo-script.pc +@CAIRO_HAS_FT_FONT_TRUE@am__append_57 = $(cairo_ft_headers) +@CAIRO_HAS_FT_FONT_TRUE@am__append_58 = $(cairo_ft_private) +@CAIRO_HAS_FT_FONT_TRUE@am__append_59 = $(cairo_ft_sources) +@CAIRO_HAS_FT_FONT_TRUE@am__append_60 = cairo-ft.pc +@CAIRO_HAS_FC_FONT_TRUE@am__append_61 = $(cairo_fc_headers) +@CAIRO_HAS_FC_FONT_TRUE@am__append_62 = $(cairo_fc_private) +@CAIRO_HAS_FC_FONT_TRUE@am__append_63 = $(cairo_fc_sources) +@CAIRO_HAS_FC_FONT_TRUE@am__append_64 = cairo-fc.pc +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_65 = $(cairo_ps_headers) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_66 = $(cairo_ps_private) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_67 = $(cairo_ps_sources) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_68 = cairo-ps.pc +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_69 = $(cairo_pdf_headers) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_70 = $(cairo_pdf_private) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_71 = $(cairo_pdf_sources) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_72 = cairo-pdf.pc +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_73 = $(cairo_svg_headers) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_74 = $(cairo_svg_private) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_75 = $(cairo_svg_sources) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_76 = cairo-svg.pc +@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_77 = $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) +@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_78 = $(cairo_test_surfaces_sources) +@CROSS_COMPILING_FALSE@am__append_79 = check-link$(EXEEXT) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/build/aclocal.cairo.m4 \ @@ -179,6 +187,7 @@ am__aclocal_m4_deps = $(top_srcdir)/build/aclocal.cairo.m4 \ $(top_srcdir)/build/aclocal.gtk-doc.m4 \ $(top_srcdir)/build/aclocal.makefile.m4 \ $(top_srcdir)/build/aclocal.pkg.m4 \ + $(top_srcdir)/build/aclocal.shave.m4 \ $(top_srcdir)/build/libtool.m4 \ $(top_srcdir)/build/ltoptions.m4 \ $(top_srcdir)/build/ltsugar.m4 \ @@ -187,10 +196,11 @@ am__aclocal_m4_deps = $(top_srcdir)/build/aclocal.cairo.m4 \ $(top_srcdir)/cairo-version.h \ $(top_srcdir)/build/configure.ac.version \ $(top_srcdir)/build/configure.ac.tools \ - $(top_srcdir)/build/configure.ac.system \ $(top_srcdir)/build/configure.ac.features \ $(top_srcdir)/build/configure.ac.warnings \ + $(top_srcdir)/build/configure.ac.system \ $(top_srcdir)/build/configure.ac.analysis \ + $(top_srcdir)/build/configure.ac.noversion \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) @@ -200,8 +210,8 @@ CONFIG_CLEAN_FILES = cairo.pc cairo-xlib.pc cairo-xlib-xrender.pc \ cairo-xcb.pc cairo-quartz.pc cairo-quartz-font.pc \ cairo-quartz-image.pc cairo-win32.pc cairo-win32-font.pc \ cairo-os2.pc cairo-beos.pc cairo-png.pc cairo-glitz.pc \ - cairo-directfb.pc cairo-ft.pc cairo-ps.pc cairo-pdf.pc \ - cairo-svg.pc + cairo-directfb.pc cairo-script.pc cairo-ft.pc cairo-fc.pc \ + cairo-ps.pc cairo-pdf.pc cairo-svg.pc am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -216,22 +226,23 @@ am__DEPENDENCIES_1 = am__libcairo_la_SOURCES_DIST = cairo.h cairo-deprecated.h cairo-xlib.h \ cairo-xlib-xrender.h cairo-xcb.h cairo-xcb-xrender.h \ cairo-quartz.h cairo-quartz-image.h cairo-win32.h cairo-os2.h \ - cairo-beos.h cairo-glitz.h cairo-directfb.h cairo-ft.h \ - cairo-ps.h cairo-pdf.h cairo-svg.h cairoint.h \ + cairo-beos.h cairo-glitz.h cairo-directfb.h cairo-script.h \ + cairo-ft.h cairo-ps.h cairo-pdf.h cairo-svg.h cairoint.h \ cairo-analysis-surface-private.h cairo-arc-private.h \ cairo-atomic-private.h cairo-cache-private.h \ - cairo-clip-private.h cairo-compiler-private.h \ - cairo-fixed-private.h cairo-fixed-type-private.h \ - cairo-freelist-private.h cairo-gstate-private.h \ - cairo-hash-private.h cairo-malloc-private.h \ + cairo-clip-private.h cairo-combsort-private.h \ + cairo-compiler-private.h cairo-fixed-private.h \ + cairo-fixed-type-private.h cairo-freelist-private.h \ + cairo-gstate-private.h cairo-hash-private.h \ + cairo-image-info-private.h cairo-malloc-private.h \ cairo-meta-surface-private.h cairo-mutex-impl-private.h \ cairo-mutex-list-private.h cairo-mutex-private.h \ cairo-mutex-type-private.h cairo-output-stream-private.h \ cairo-paginated-private.h cairo-paginated-surface-private.h \ cairo-path-fixed-private.h cairo-path-private.h \ cairo-private.h cairo-reference-count-private.h \ - cairo-region-private.h cairo-scaled-font-private.h \ - cairo-skiplist-private.h cairo-surface-fallback-private.h \ + cairo-scaled-font-private.h cairo-skiplist-private.h \ + cairo-spans-private.h cairo-surface-fallback-private.h \ cairo-surface-private.h cairo-types-private.h \ cairo-user-font-private.h cairo-wideint-private.h \ cairo-wideint-type-private.h \ @@ -244,36 +255,39 @@ am__libcairo_la_SOURCES_DIST = cairo.h cairo-deprecated.h cairo-xlib.h \ cairo-os2-private.h cairo-glitz-private.h cairo-ft-private.h \ cairo-ps-surface-private.h cairo-pdf-surface-private.h \ cairo-svg-surface-private.h test-fallback-surface.h \ - test-meta-surface.h test-paginated-surface.h \ - cairo-analysis-surface.c cairo-arc.c cairo-array.c \ - cairo-atomic.c cairo-base85-stream.c cairo-bentley-ottmann.c \ - cairo.c cairo-cache.c cairo-clip.c cairo-color.c cairo-debug.c \ - cairo-fixed.c cairo-font-face.c cairo-font-face-twin.c \ - cairo-font-face-twin-data.c cairo-font-options.c \ - cairo-freelist.c cairo-gstate.c cairo-hash.c cairo-hull.c \ + test-fallback16-surface.h test-meta-surface.h \ + test-paginated-surface.h cairo-analysis-surface.c cairo-arc.c \ + cairo-array.c cairo-atomic.c cairo-base85-stream.c \ + cairo-bentley-ottmann.c cairo.c cairo-cache.c cairo-clip.c \ + cairo-color.c cairo-debug.c cairo-fixed.c cairo-font-face.c \ + cairo-font-face-twin.c cairo-font-face-twin-data.c \ + cairo-font-options.c cairo-freelist.c cairo-gstate.c \ + cairo-hash.c cairo-hull.c cairo-image-info.c \ cairo-image-surface.c cairo-lzw.c cairo-matrix.c \ cairo-meta-surface.c cairo-misc.c cairo-mutex.c \ cairo-output-stream.c cairo-paginated-surface.c \ cairo-path-bounds.c cairo-path.c cairo-path-fill.c \ - cairo-path-fixed.c cairo-path-stroke.c cairo-pattern.c \ - cairo-pen.c cairo-polygon.c cairo-rectangle.c cairo-region.c \ - cairo-scaled-font.c cairo-skiplist.c cairo-slope.c \ - cairo-spline.c cairo-stroke-style.c cairo-surface.c \ - cairo-surface-fallback.c cairo-system.c cairo-traps.c \ - cairo-unicode.c cairo-user-font.c cairo-version.c \ - cairo-wideint.c cairo-cff-subset.c cairo-scaled-font-subsets.c \ - cairo-truetype-subset.c cairo-type1-fallback.c \ - cairo-type1-subset.c cairo-type3-glyph-surface.c \ - cairo-pdf-operators.c cairo-xlib-display.c cairo-xlib-screen.c \ - cairo-xlib-surface.c cairo-xlib-visual.c cairo-xcb-surface.c \ - cairo-quartz-surface.c cairo-quartz-font.c \ - cairo-quartz-image-surface.c cairo-win32-surface.c \ - cairo-win32-printing-surface.c cairo-win32-font.c \ - cairo-os2-surface.c cairo-png.c cairo-glitz-surface.c \ - cairo-directfb-surface.c cairo-ft-font.c cairo-ps-surface.c \ + cairo-path-fixed.c cairo-path-in-fill.c cairo-path-stroke.c \ + cairo-pattern.c cairo-pen.c cairo-polygon.c cairo-rectangle.c \ + cairo-region.c cairo-scaled-font.c cairo-skiplist.c \ + cairo-slope.c cairo-spans.c cairo-spline.c \ + cairo-stroke-style.c cairo-surface.c cairo-surface-fallback.c \ + cairo-tor-scan-converter.c cairo-system.c cairo-traps.c \ + cairo-toy-font-face.c cairo-unicode.c cairo-user-font.c \ + cairo-version.c cairo-wideint.c cairo-cff-subset.c \ + cairo-scaled-font-subsets.c cairo-truetype-subset.c \ + cairo-type1-fallback.c cairo-type1-subset.c \ + cairo-type3-glyph-surface.c cairo-pdf-operators.c \ + cairo-xlib-display.c cairo-xlib-screen.c cairo-xlib-surface.c \ + cairo-xlib-visual.c cairo-xcb-surface.c cairo-quartz-surface.c \ + cairo-quartz-font.c cairo-quartz-image-surface.c \ + cairo-win32-surface.c cairo-win32-printing-surface.c \ + cairo-win32-font.c cairo-os2-surface.c cairo-png.c \ + cairo-glitz-surface.c cairo-directfb-surface.c \ + cairo-script-surface.c cairo-ft-font.c cairo-ps-surface.c \ cairo-pdf-surface.c cairo-deflate-stream.c cairo-svg-surface.c \ - test-fallback-surface.c test-meta-surface.c \ - test-paginated-surface.c cairo-version.h cairo-features.h + test-fallback-surface.c test-fallback16-surface.c \ + test-meta-surface.c test-paginated-surface.c cairo-version.h am__objects_1 = @CAIRO_HAS_XLIB_SURFACE_TRUE@am__objects_2 = $(am__objects_1) @CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__objects_3 = $(am__objects_1) @@ -285,100 +299,111 @@ am__objects_1 = @CAIRO_HAS_BEOS_SURFACE_TRUE@am__objects_9 = $(am__objects_1) @CAIRO_HAS_GLITZ_SURFACE_TRUE@am__objects_10 = $(am__objects_1) @CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__objects_11 = $(am__objects_1) -@CAIRO_HAS_FT_FONT_TRUE@am__objects_12 = $(am__objects_1) -@CAIRO_HAS_PS_SURFACE_TRUE@am__objects_13 = $(am__objects_1) -@CAIRO_HAS_PDF_SURFACE_TRUE@am__objects_14 = $(am__objects_1) -@CAIRO_HAS_SVG_SURFACE_TRUE@am__objects_15 = $(am__objects_1) -am__objects_16 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__objects_12 = $(am__objects_1) +@CAIRO_HAS_FT_FONT_TRUE@am__objects_13 = $(am__objects_1) +@CAIRO_HAS_PS_SURFACE_TRUE@am__objects_14 = $(am__objects_1) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__objects_15 = $(am__objects_1) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__objects_16 = $(am__objects_1) +am__objects_17 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ $(am__objects_4) $(am__objects_5) $(am__objects_1) \ $(am__objects_6) $(am__objects_7) $(am__objects_1) \ $(am__objects_8) $(am__objects_9) $(am__objects_1) \ $(am__objects_10) $(am__objects_11) $(am__objects_12) \ - $(am__objects_13) $(am__objects_14) $(am__objects_15) -am__objects_17 = $(am__objects_1) $(am__objects_1) -@CAIRO_HAS_TEST_SURFACES_TRUE@am__objects_18 = $(am__objects_1) -am__objects_19 = $(am__objects_17) $(am__objects_2) $(am__objects_1) \ + $(am__objects_13) $(am__objects_1) $(am__objects_14) \ + $(am__objects_15) $(am__objects_16) +am__objects_18 = $(am__objects_1) $(am__objects_1) +@CAIRO_HAS_TEST_SURFACES_TRUE@am__objects_19 = $(am__objects_1) +am__objects_20 = $(am__objects_18) $(am__objects_2) $(am__objects_1) \ $(am__objects_1) $(am__objects_5) $(am__objects_1) \ $(am__objects_1) $(am__objects_7) $(am__objects_1) \ $(am__objects_8) $(am__objects_1) $(am__objects_1) \ - $(am__objects_10) $(am__objects_1) $(am__objects_12) \ - $(am__objects_13) $(am__objects_14) $(am__objects_15) \ - $(am__objects_18) -am__objects_20 = cairo-cff-subset.lo cairo-scaled-font-subsets.lo \ + $(am__objects_10) $(am__objects_1) $(am__objects_1) \ + $(am__objects_13) $(am__objects_1) $(am__objects_14) \ + $(am__objects_15) $(am__objects_16) $(am__objects_19) +am__objects_21 = cairo-cff-subset.lo cairo-scaled-font-subsets.lo \ cairo-truetype-subset.lo cairo-type1-fallback.lo \ cairo-type1-subset.lo cairo-type3-glyph-surface.lo -am__objects_21 = cairo-pdf-operators.lo -am__objects_22 = cairo-analysis-surface.lo cairo-arc.lo cairo-array.lo \ +am__objects_22 = cairo-pdf-operators.lo +am__objects_23 = cairo-analysis-surface.lo cairo-arc.lo cairo-array.lo \ cairo-atomic.lo cairo-base85-stream.lo \ cairo-bentley-ottmann.lo cairo.lo cairo-cache.lo cairo-clip.lo \ cairo-color.lo cairo-debug.lo cairo-fixed.lo \ cairo-font-face.lo cairo-font-face-twin.lo \ cairo-font-face-twin-data.lo cairo-font-options.lo \ cairo-freelist.lo cairo-gstate.lo cairo-hash.lo cairo-hull.lo \ - cairo-image-surface.lo cairo-lzw.lo cairo-matrix.lo \ - cairo-meta-surface.lo cairo-misc.lo cairo-mutex.lo \ - cairo-output-stream.lo cairo-paginated-surface.lo \ - cairo-path-bounds.lo cairo-path.lo cairo-path-fill.lo \ - cairo-path-fixed.lo cairo-path-stroke.lo cairo-pattern.lo \ - cairo-pen.lo cairo-polygon.lo cairo-rectangle.lo \ - cairo-region.lo cairo-scaled-font.lo cairo-skiplist.lo \ - cairo-slope.lo cairo-spline.lo cairo-stroke-style.lo \ - cairo-surface.lo cairo-surface-fallback.lo cairo-system.lo \ - cairo-traps.lo cairo-unicode.lo cairo-user-font.lo \ - cairo-version.lo cairo-wideint.lo $(am__objects_20) \ - $(am__objects_21) -am__objects_23 = cairo-xlib-display.lo cairo-xlib-screen.lo \ + cairo-image-info.lo cairo-image-surface.lo cairo-lzw.lo \ + cairo-matrix.lo cairo-meta-surface.lo cairo-misc.lo \ + cairo-mutex.lo cairo-output-stream.lo \ + cairo-paginated-surface.lo cairo-path-bounds.lo cairo-path.lo \ + cairo-path-fill.lo cairo-path-fixed.lo cairo-path-in-fill.lo \ + cairo-path-stroke.lo cairo-pattern.lo cairo-pen.lo \ + cairo-polygon.lo cairo-rectangle.lo cairo-region.lo \ + cairo-scaled-font.lo cairo-skiplist.lo cairo-slope.lo \ + cairo-spans.lo cairo-spline.lo cairo-stroke-style.lo \ + cairo-surface.lo cairo-surface-fallback.lo \ + cairo-tor-scan-converter.lo cairo-system.lo cairo-traps.lo \ + cairo-toy-font-face.lo cairo-unicode.lo cairo-user-font.lo \ + cairo-version.lo cairo-wideint.lo $(am__objects_21) \ + $(am__objects_22) +am__objects_24 = cairo-xlib-display.lo cairo-xlib-screen.lo \ cairo-xlib-surface.lo cairo-xlib-visual.lo -@CAIRO_HAS_XLIB_SURFACE_TRUE@am__objects_24 = $(am__objects_23) -am__objects_25 = cairo-xcb-surface.lo -@CAIRO_HAS_XCB_SURFACE_TRUE@am__objects_26 = $(am__objects_25) -am__objects_27 = cairo-quartz-surface.lo -@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__objects_28 = $(am__objects_27) -am__objects_29 = cairo-quartz-font.lo -@CAIRO_HAS_QUARTZ_FONT_TRUE@am__objects_30 = $(am__objects_29) -am__objects_31 = cairo-quartz-image-surface.lo -@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__objects_32 = \ -@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@ $(am__objects_31) -am__objects_33 = cairo-win32-surface.lo \ +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__objects_25 = $(am__objects_24) +am__objects_26 = cairo-xcb-surface.lo +@CAIRO_HAS_XCB_SURFACE_TRUE@am__objects_27 = $(am__objects_26) +am__objects_28 = cairo-quartz-surface.lo +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__objects_29 = $(am__objects_28) +am__objects_30 = cairo-quartz-font.lo +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__objects_31 = $(am__objects_30) +am__objects_32 = cairo-quartz-image-surface.lo +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__objects_33 = \ +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@ $(am__objects_32) +am__objects_34 = cairo-win32-surface.lo \ cairo-win32-printing-surface.lo -@CAIRO_HAS_WIN32_SURFACE_TRUE@am__objects_34 = $(am__objects_33) -am__objects_35 = cairo-win32-font.lo -@CAIRO_HAS_WIN32_FONT_TRUE@am__objects_36 = $(am__objects_35) -am__objects_37 = cairo-os2-surface.lo -@CAIRO_HAS_OS2_SURFACE_TRUE@am__objects_38 = $(am__objects_37) -am__objects_39 = cairo-png.lo -@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__objects_40 = $(am__objects_39) -am__objects_41 = cairo-glitz-surface.lo -@CAIRO_HAS_GLITZ_SURFACE_TRUE@am__objects_42 = $(am__objects_41) -am__objects_43 = cairo-directfb-surface.lo -@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__objects_44 = $(am__objects_43) -am__objects_45 = cairo-ft-font.lo -@CAIRO_HAS_FT_FONT_TRUE@am__objects_46 = $(am__objects_45) -am__objects_47 = cairo-ps-surface.lo -@CAIRO_HAS_PS_SURFACE_TRUE@am__objects_48 = $(am__objects_47) -am__objects_49 = cairo-pdf-surface.lo cairo-deflate-stream.lo -@CAIRO_HAS_PDF_SURFACE_TRUE@am__objects_50 = $(am__objects_49) -am__objects_51 = cairo-svg-surface.lo -@CAIRO_HAS_SVG_SURFACE_TRUE@am__objects_52 = $(am__objects_51) -am__objects_53 = test-fallback-surface.lo test-meta-surface.lo \ - test-paginated-surface.lo -@CAIRO_HAS_TEST_SURFACES_TRUE@am__objects_54 = $(am__objects_53) -am__objects_55 = $(am__objects_22) $(am__objects_24) $(am__objects_1) \ - $(am__objects_26) $(am__objects_28) $(am__objects_30) \ - $(am__objects_32) $(am__objects_34) $(am__objects_36) \ - $(am__objects_38) $(am__objects_1) $(am__objects_40) \ - $(am__objects_42) $(am__objects_44) $(am__objects_46) \ - $(am__objects_48) $(am__objects_50) $(am__objects_52) \ - $(am__objects_54) -am_libcairo_la_OBJECTS = $(am__objects_16) $(am__objects_19) \ - $(am__objects_55) -libcairo_la_OBJECTS = $(am_libcairo_la_OBJECTS) +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__objects_35 = $(am__objects_34) +am__objects_36 = cairo-win32-font.lo +@CAIRO_HAS_WIN32_FONT_TRUE@am__objects_37 = $(am__objects_36) +am__objects_38 = cairo-os2-surface.lo +@CAIRO_HAS_OS2_SURFACE_TRUE@am__objects_39 = $(am__objects_38) +am__objects_40 = cairo-png.lo +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__objects_41 = $(am__objects_40) +am__objects_42 = cairo-glitz-surface.lo +@CAIRO_HAS_GLITZ_SURFACE_TRUE@am__objects_43 = $(am__objects_42) +am__objects_44 = cairo-directfb-surface.lo +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__objects_45 = $(am__objects_44) +am__objects_46 = cairo-script-surface.lo +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__objects_47 = $(am__objects_46) +am__objects_48 = cairo-ft-font.lo +@CAIRO_HAS_FT_FONT_TRUE@am__objects_49 = $(am__objects_48) +am__objects_50 = cairo-ps-surface.lo +@CAIRO_HAS_PS_SURFACE_TRUE@am__objects_51 = $(am__objects_50) +am__objects_52 = cairo-pdf-surface.lo cairo-deflate-stream.lo +@CAIRO_HAS_PDF_SURFACE_TRUE@am__objects_53 = $(am__objects_52) +am__objects_54 = cairo-svg-surface.lo +@CAIRO_HAS_SVG_SURFACE_TRUE@am__objects_55 = $(am__objects_54) +am__objects_56 = test-fallback-surface.lo test-fallback16-surface.lo \ + test-meta-surface.lo test-paginated-surface.lo +@CAIRO_HAS_TEST_SURFACES_TRUE@am__objects_57 = $(am__objects_56) +am__objects_58 = $(am__objects_23) $(am__objects_25) $(am__objects_1) \ + $(am__objects_27) $(am__objects_29) $(am__objects_31) \ + $(am__objects_33) $(am__objects_35) $(am__objects_37) \ + $(am__objects_39) $(am__objects_1) $(am__objects_41) \ + $(am__objects_43) $(am__objects_45) $(am__objects_47) \ + $(am__objects_49) $(am__objects_1) $(am__objects_51) \ + $(am__objects_53) $(am__objects_55) $(am__objects_57) +am_libcairo_la_OBJECTS = $(am__objects_17) $(am__objects_20) \ + $(am__objects_58) +nodist_libcairo_la_OBJECTS = +libcairo_la_OBJECTS = $(am_libcairo_la_OBJECTS) \ + $(nodist_libcairo_la_OBJECTS) libcairo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libcairo_la_LDFLAGS) $(LDFLAGS) -o $@ check_link_SOURCES = check-link.c check_link_OBJECTS = check-link.$(OBJEXT) check_link_DEPENDENCIES = libcairo.la +am_check_skiplist_OBJECTS = check_skiplist-cairo-skiplist.$(OBJEXT) +check_skiplist_OBJECTS = $(am_check_skiplist_OBJECTS) +check_skiplist_LDADD = $(LDADD) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/build/depcomp am__depfiles_maybe = depfiles @@ -388,16 +413,18 @@ CCLD = $(CC) LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ -SOURCES = $(libcairo_la_SOURCES) check-link.c -DIST_SOURCES = $(am__libcairo_la_SOURCES_DIST) check-link.c +SOURCES = $(libcairo_la_SOURCES) $(nodist_libcairo_la_SOURCES) \ + check-link.c $(check_skiplist_SOURCES) +DIST_SOURCES = $(am__libcairo_la_SOURCES_DIST) check-link.c \ + $(check_skiplist_SOURCES) pkgconfigDATA_INSTALL = $(INSTALL_DATA) DATA = $(pkgconfig_DATA) am__cairoinclude_HEADERS_DIST = cairo.h cairo-deprecated.h \ cairo-xlib.h cairo-xlib-xrender.h cairo-xcb.h \ cairo-xcb-xrender.h cairo-quartz.h cairo-quartz-image.h \ cairo-win32.h cairo-os2.h cairo-beos.h cairo-glitz.h \ - cairo-directfb.h cairo-ft.h cairo-ps.h cairo-pdf.h cairo-svg.h \ - $(top_srcdir)/cairo-version.h + cairo-directfb.h cairo-script.h cairo-ft.h cairo-ps.h \ + cairo-pdf.h cairo-svg.h $(top_srcdir)/cairo-version.h cairoincludeHEADERS_INSTALL = $(INSTALL_HEADER) nodist_cairoincludeHEADERS_INSTALL = $(INSTALL_HEADER) HEADERS = $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) @@ -408,20 +435,24 @@ am__EXEEXT_1 = check-def.sh check-doc-syntax.sh check-headers.sh \ DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_MAKEFLAGS = @AM_MAKEFLAGS@ AR = @AR@ AS = @AS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFD_LIBS = @BFD_LIBS@ CAIROPERF_LIBS = @CAIROPERF_LIBS@ CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LDFLAGS = @CAIRO_LDFLAGS@ CAIRO_LIBS = @CAIRO_LIBS@ CAIRO_LIBTOOL_VERSION_INFO = @CAIRO_LIBTOOL_VERSION_INFO@ CAIRO_NONPKGCONFIG_CFLAGS = @CAIRO_NONPKGCONFIG_CFLAGS@ CAIRO_NONPKGCONFIG_LIBS = @CAIRO_NONPKGCONFIG_LIBS@ CAIRO_RELEASE_STATUS = @CAIRO_RELEASE_STATUS@ CAIRO_REQUIRES = @CAIRO_REQUIRES@ +CAIRO_TEST_MODE = @CAIRO_TEST_MODE@ CAIRO_TEST_UNDEFINED_LDFLAGS = @CAIRO_TEST_UNDEFINED_LDFLAGS@ CAIRO_VERSION_MAJOR = @CAIRO_VERSION_MAJOR@ CAIRO_VERSION_MICRO = @CAIRO_VERSION_MICRO@ @@ -448,6 +479,8 @@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ +F77 = @F77@ +FC = @FC@ FGREP = @FGREP@ FIND = @FIND@ FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@ @@ -489,10 +522,12 @@ LTCXXCOMPILE = @LTCXXCOMPILE@ LTLIBOBJS = @LTLIBOBJS@ LTP = @LTP@ LTP_GENHTML = @LTP_GENHTML@ +MAKEFLAGS = @MAKEFLAGS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ +OBJC = @OBJC@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -508,12 +543,14 @@ PKGCONFIG_REQUIRES = @PKGCONFIG_REQUIRES@ PKG_CONFIG = @PKG_CONFIG@ POPPLER_CFLAGS = @POPPLER_CFLAGS@ POPPLER_LIBS = @POPPLER_LIBS@ +Q = @Q@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SHTOOL = @SHTOOL@ STRIP = @STRIP@ +V = @V@ VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ VALGRIND_LIBS = @VALGRIND_LIBS@ VERSION = @VERSION@ @@ -581,6 +618,7 @@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ +shavedir = @shavedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ @@ -596,28 +634,28 @@ xlib_xrender_LIBS = @xlib_xrender_LIBS@ BUILT_SOURCES = cairo-features.h cairo-supported-features.h CLEANFILES = *.i *.s *.gch $(EXTRA_LTLIBRARIES) $(EXTRA_PROGRAMS) \ $(check_PROGRAMS) cairo.def headers-standalone -DISTCLEANFILES = $(BUILT_SOURCES) -EXTRA_DIST = Makefile.win32 Makefile.win32.features \ - cairo-supported-features.h $(TESTS_SH) \ +DISTCLEANFILES = $(BUILT_SOURCES) cairo-features.h \ + cairo-supported-features.h +EXTRA_DIST = Makefile.win32 Makefile.win32.features $(TESTS_SH) \ check-has-hidden-symbols.c EXTRA_LTLIBRARIES = -MAINTAINERCLEANFILES = Makefile.in $(srcdir)/Makefile.win32.features \ - cairo-supported-features.h +MAINTAINERCLEANFILES = Makefile.in cairo_headers = cairo.h cairo-deprecated.h cairo_private = cairoint.h cairo-analysis-surface-private.h \ cairo-arc-private.h cairo-atomic-private.h \ cairo-cache-private.h cairo-clip-private.h \ - cairo-compiler-private.h cairo-fixed-private.h \ - cairo-fixed-type-private.h cairo-freelist-private.h \ - cairo-gstate-private.h cairo-hash-private.h \ + cairo-combsort-private.h cairo-compiler-private.h \ + cairo-fixed-private.h cairo-fixed-type-private.h \ + cairo-freelist-private.h cairo-gstate-private.h \ + cairo-hash-private.h cairo-image-info-private.h \ cairo-malloc-private.h cairo-meta-surface-private.h \ cairo-mutex-impl-private.h cairo-mutex-list-private.h \ cairo-mutex-private.h cairo-mutex-type-private.h \ cairo-output-stream-private.h cairo-paginated-private.h \ cairo-paginated-surface-private.h cairo-path-fixed-private.h \ cairo-path-private.h cairo-private.h \ - cairo-reference-count-private.h cairo-region-private.h \ - cairo-scaled-font-private.h cairo-skiplist-private.h \ + cairo-reference-count-private.h cairo-scaled-font-private.h \ + cairo-skiplist-private.h cairo-spans-private.h \ cairo-surface-fallback-private.h cairo-surface-private.h \ cairo-types-private.h cairo-user-font-private.h \ cairo-wideint-private.h cairo-wideint-type-private.h $(NULL) \ @@ -628,18 +666,19 @@ cairo_sources = cairo-analysis-surface.c cairo-arc.c cairo-array.c \ cairo-fixed.c cairo-font-face.c cairo-font-face-twin.c \ cairo-font-face-twin-data.c cairo-font-options.c \ cairo-freelist.c cairo-gstate.c cairo-hash.c cairo-hull.c \ - cairo-image-surface.c cairo-lzw.c cairo-matrix.c \ - cairo-meta-surface.c cairo-misc.c cairo-mutex.c \ + cairo-image-info.c cairo-image-surface.c cairo-lzw.c \ + cairo-matrix.c cairo-meta-surface.c cairo-misc.c cairo-mutex.c \ cairo-output-stream.c cairo-paginated-surface.c \ cairo-path-bounds.c cairo-path.c cairo-path-fill.c \ - cairo-path-fixed.c cairo-path-stroke.c cairo-pattern.c \ - cairo-pen.c cairo-polygon.c cairo-rectangle.c cairo-region.c \ - cairo-scaled-font.c cairo-skiplist.c cairo-slope.c \ - cairo-spline.c cairo-stroke-style.c cairo-surface.c \ - cairo-surface-fallback.c cairo-system.c cairo-traps.c \ - cairo-unicode.c cairo-user-font.c cairo-version.c \ - cairo-wideint.c $(NULL) $(_cairo_font_subset_sources) \ - $(_cairo_pdf_operators_sources) + cairo-path-fixed.c cairo-path-in-fill.c cairo-path-stroke.c \ + cairo-pattern.c cairo-pen.c cairo-polygon.c cairo-rectangle.c \ + cairo-region.c cairo-scaled-font.c cairo-skiplist.c \ + cairo-slope.c cairo-spans.c cairo-spline.c \ + cairo-stroke-style.c cairo-surface.c cairo-surface-fallback.c \ + cairo-tor-scan-converter.c cairo-system.c cairo-traps.c \ + cairo-toy-font-face.c cairo-unicode.c cairo-user-font.c \ + cairo-version.c cairo-wideint.c $(NULL) \ + $(_cairo_font_subset_sources) $(_cairo_pdf_operators_sources) _cairo_font_subset_private = \ cairo-scaled-font-subsets-private.h \ cairo-truetype-subset-private.h \ @@ -675,12 +714,14 @@ cairo_ft_sources = cairo-ft-font.c # These are private, even though they look like public headers cairo_test_surfaces_private = \ test-fallback-surface.h \ + test-fallback16-surface.h \ test-meta-surface.h \ test-paginated-surface.h \ $(NULL) cairo_test_surfaces_sources = \ test-fallback-surface.c \ + test-fallback16-surface.c \ test-meta-surface.c \ test-paginated-surface.c \ $(NULL) @@ -728,17 +769,19 @@ cairo_glitz_private = cairo-glitz-private.h cairo_glitz_sources = cairo-glitz-surface.c cairo_directfb_headers = cairo-directfb.h cairo_directfb_sources = cairo-directfb-surface.c +cairo_script_headers = cairo-script.h +cairo_script_sources = cairo-script-surface.c supported_cairo_headers = $(cairo_headers) $(cairo_xlib_headers) \ $(cairo_xlib_xrender_headers) $(cairo_quartz_headers) \ $(cairo_quartz_font_headers) $(cairo_win32_headers) \ $(cairo_win32_font_headers) $(cairo_png_headers) \ - $(cairo_ft_headers) $(cairo_ps_headers) $(cairo_pdf_headers) \ - $(cairo_svg_headers) $(cairo_image_headers) \ - $(cairo_user_headers) + $(cairo_ft_headers) $(cairo_fc_headers) $(cairo_ps_headers) \ + $(cairo_pdf_headers) $(cairo_svg_headers) \ + $(cairo_image_headers) $(cairo_user_headers) unsupported_cairo_headers = $(cairo_xcb_headers) \ $(cairo_quartz_image_headers) $(cairo_os2_headers) \ $(cairo_beos_headers) $(cairo_glitz_headers) \ - $(cairo_directfb_headers) + $(cairo_directfb_headers) $(cairo_script_headers) all_cairo_files = $(all_cairo_headers) $(all_cairo_private) $(all_cairo_sources) all_cairo_headers = $(cairo_headers) $(cairo_xlib_headers) \ $(cairo_xlib_xrender_headers) $(cairo_xcb_headers) \ @@ -747,7 +790,8 @@ all_cairo_headers = $(cairo_headers) $(cairo_xlib_headers) \ $(cairo_win32_font_headers) $(cairo_os2_headers) \ $(cairo_beos_headers) $(cairo_png_headers) \ $(cairo_glitz_headers) $(cairo_directfb_headers) \ - $(cairo_ft_headers) $(cairo_ps_headers) $(cairo_pdf_headers) \ + $(cairo_script_headers) $(cairo_ft_headers) \ + $(cairo_fc_headers) $(cairo_ps_headers) $(cairo_pdf_headers) \ $(cairo_svg_headers) $(cairo_image_headers) \ $(cairo_user_headers) all_cairo_private = $(cairo_private) $(cairo_xlib_private) \ @@ -757,7 +801,8 @@ all_cairo_private = $(cairo_private) $(cairo_xlib_private) \ $(cairo_win32_font_private) $(cairo_os2_private) \ $(cairo_beos_private) $(cairo_png_private) \ $(cairo_glitz_private) $(cairo_directfb_private) \ - $(cairo_ft_private) $(cairo_ps_private) $(cairo_pdf_private) \ + $(cairo_script_private) $(cairo_ft_private) \ + $(cairo_fc_private) $(cairo_ps_private) $(cairo_pdf_private) \ $(cairo_svg_private) $(cairo_test_surfaces_private) \ $(cairo_test_surfaces_headers) $(cairo_image_private) \ $(cairo_user_private) @@ -768,7 +813,8 @@ all_cairo_sources = $(cairo_sources) $(cairo_xlib_sources) \ $(cairo_win32_font_sources) $(cairo_os2_sources) \ $(cairo_beos_sources) $(cairo_png_sources) \ $(cairo_glitz_sources) $(cairo_directfb_sources) \ - $(cairo_ft_sources) $(cairo_ps_sources) $(cairo_pdf_sources) \ + $(cairo_script_sources) $(cairo_ft_sources) \ + $(cairo_fc_sources) $(cairo_ps_sources) $(cairo_pdf_sources) \ $(cairo_svg_sources) $(cairo_test_surfaces_sources) \ $(cairo_image_sources) $(cairo_user_sources) enabled_cairo_files = $(enabled_cairo_headers) $(enabled_cairo_private) $(enabled_cairo_sources) @@ -778,36 +824,40 @@ enabled_cairo_headers = $(cairo_headers) $(am__append_1) \ $(am__append_29) $(am__append_33) $(am__append_37) \ $(am__append_41) $(am__append_45) $(am__append_49) \ $(am__append_53) $(am__append_57) $(am__append_61) \ - $(am__append_65) $(cairo_image_headers) $(cairo_user_headers) + $(am__append_65) $(am__append_69) $(am__append_73) \ + $(cairo_image_headers) $(cairo_user_headers) enabled_cairo_private = $(cairo_private) $(am__append_2) \ $(am__append_6) $(am__append_10) $(am__append_14) \ $(am__append_18) $(am__append_22) $(am__append_26) \ $(am__append_30) $(am__append_34) $(am__append_38) \ $(am__append_42) $(am__append_46) $(am__append_50) \ $(am__append_54) $(am__append_58) $(am__append_62) \ - $(am__append_66) $(am__append_69) $(cairo_image_private) \ - $(cairo_user_private) + $(am__append_66) $(am__append_70) $(am__append_74) \ + $(am__append_77) $(cairo_image_private) $(cairo_user_private) enabled_cairo_sources = $(cairo_sources) $(am__append_3) \ $(am__append_7) $(am__append_11) $(am__append_15) \ $(am__append_19) $(am__append_23) $(am__append_27) \ $(am__append_31) $(am__append_35) $(am__append_39) \ $(am__append_43) $(am__append_47) $(am__append_51) \ $(am__append_55) $(am__append_59) $(am__append_63) \ - $(am__append_67) $(am__append_70) $(cairo_image_sources) \ - $(cairo_user_sources) + $(am__append_67) $(am__append_71) $(am__append_75) \ + $(am__append_78) $(cairo_image_sources) $(cairo_user_sources) all_cairo_pkgconf = cairo.pc cairo-xlib.pc cairo-xlib-xrender.pc \ cairo-xcb.pc cairo-quartz.pc cairo-quartz-font.pc \ cairo-quartz-image.pc cairo-win32.pc cairo-win32-font.pc \ cairo-os2.pc cairo-beos.pc cairo-png.pc cairo-glitz.pc \ - cairo-directfb.pc cairo-ft.pc cairo-ps.pc cairo-pdf.pc \ - cairo-svg.pc + cairo-directfb.pc cairo-script.pc cairo-ft.pc cairo-fc.pc \ + cairo-ps.pc cairo-pdf.pc cairo-svg.pc enabled_cairo_pkgconf = cairo.pc $(am__append_4) $(am__append_8) \ $(am__append_12) $(am__append_16) $(am__append_20) \ $(am__append_24) $(am__append_28) $(am__append_32) \ $(am__append_36) $(am__append_40) $(am__append_44) \ $(am__append_48) $(am__append_52) $(am__append_56) \ - $(am__append_60) $(am__append_64) $(am__append_68) + $(am__append_60) $(am__append_64) $(am__append_68) \ + $(am__append_72) $(am__append_76) +#MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features AM_CPPFLAGS = -I$(srcdir) $(CAIRO_CFLAGS) +AM_LDFLAGS = $(CAIRO_LDFLAGS) @OS_WIN32_TRUE@export_symbols = -export-symbols cairo.def @OS_WIN32_TRUE@cairo_def_dependency = cairo.def cairoincludedir = $(includedir)/cairo @@ -818,11 +868,12 @@ cairoinclude_HEADERS = $(enabled_cairo_headers) \ lib_LTLIBRARIES = libcairo.la libcairo_la_SOURCES = $(enabled_cairo_headers) \ $(enabled_cairo_private) $(enabled_cairo_sources) $(NULL) \ - cairo-version.h cairo-features.h -libcairo_la_LDFLAGS = -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols) + cairo-version.h +libcairo_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(CAIRO_LIBTOOL_VERSION_INFO) -no-undefined $(export_symbols) libcairo_la_LIBADD = $(CAIRO_LIBS) libcairo_la_DEPENDENCIES = $(cairo_def_dependency) nodist_cairoinclude_HEADERS = cairo-features.h +nodist_libcairo_la_SOURCES = cairo-features.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = $(enabled_cairo_pkgconf) TESTS_ENVIRONMENT = \ @@ -847,6 +898,8 @@ TESTS_SH = \ $(NULL) check_link_LDADD = libcairo.la +check_skiplist_SOURCES = cairo-skiplist.c +check_skiplist_CPPFLAGS = -DMAIN $(AM_CPPFLAGS) PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS) SPARSE = sparse @@ -914,8 +967,12 @@ cairo-glitz.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ cairo-directfb.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-script.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ cairo-ft.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-fc.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ cairo-ps.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ cairo-pdf.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in @@ -961,6 +1018,9 @@ clean-checkPROGRAMS: check-link$(EXEEXT): $(check_link_OBJECTS) $(check_link_DEPENDENCIES) @rm -f check-link$(EXEEXT) $(LINK) $(check_link_OBJECTS) $(check_link_LDADD) $(LIBS) +check-skiplist$(EXEEXT): $(check_skiplist_OBJECTS) $(check_skiplist_DEPENDENCIES) + @rm -f check-skiplist$(EXEEXT) + $(LINK) $(check_skiplist_OBJECTS) $(check_skiplist_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -992,6 +1052,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gstate.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-hull.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-image-info.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-image-surface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-lzw.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-matrix.Plo@am__quote@ @@ -1004,6 +1065,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-bounds.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-fill.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-fixed.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-in-fill.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-stroke.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pattern.Plo@am__quote@ @@ -1020,14 +1082,18 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-region.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-scaled-font-subsets.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-scaled-font.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-script-surface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-skiplist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-slope.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-spans.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-spline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-stroke-style.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-fallback.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-svg-surface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-system.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tor-scan-converter.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-toy-font-face.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-traps.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-truetype-subset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-type1-fallback.Plo@am__quote@ @@ -1047,7 +1113,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-visual.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check-link.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_skiplist-cairo-skiplist.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fallback-surface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fallback16-surface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-meta-surface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-paginated-surface.Plo@am__quote@ @@ -1072,6 +1140,20 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +check_skiplist-cairo-skiplist.o: cairo-skiplist.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(check_skiplist_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT check_skiplist-cairo-skiplist.o -MD -MP -MF $(DEPDIR)/check_skiplist-cairo-skiplist.Tpo -c -o check_skiplist-cairo-skiplist.o `test -f 'cairo-skiplist.c' || echo '$(srcdir)/'`cairo-skiplist.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/check_skiplist-cairo-skiplist.Tpo $(DEPDIR)/check_skiplist-cairo-skiplist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cairo-skiplist.c' object='check_skiplist-cairo-skiplist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(check_skiplist_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o check_skiplist-cairo-skiplist.o `test -f 'cairo-skiplist.c' || echo '$(srcdir)/'`cairo-skiplist.c + +check_skiplist-cairo-skiplist.obj: cairo-skiplist.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(check_skiplist_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT check_skiplist-cairo-skiplist.obj -MD -MP -MF $(DEPDIR)/check_skiplist-cairo-skiplist.Tpo -c -o check_skiplist-cairo-skiplist.obj `if test -f 'cairo-skiplist.c'; then $(CYGPATH_W) 'cairo-skiplist.c'; else $(CYGPATH_W) '$(srcdir)/cairo-skiplist.c'; fi` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/check_skiplist-cairo-skiplist.Tpo $(DEPDIR)/check_skiplist-cairo-skiplist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cairo-skiplist.c' object='check_skiplist-cairo-skiplist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(check_skiplist_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o check_skiplist-cairo-skiplist.obj `if test -f 'cairo-skiplist.c'; then $(CYGPATH_W) 'cairo-skiplist.c'; else $(CYGPATH_W) '$(srcdir)/cairo-skiplist.c'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -1451,7 +1533,7 @@ uno: headers-standalone: $(enabled_cairo_headers) $(enabled_cairo_private) @echo Checking that enabled public/private headers can be compiled standalone @status=true; for f in $(enabled_cairo_headers) $(enabled_cairo_private); do \ - echo $(COMPILE) -o /dev/null $(srcdir)/$$f; \ + echo " CHECK $$f"; \ $(COMPILE) -o /dev/null $(srcdir)/$$f || status=false; \ done; $$status @touch $@ diff --git a/src/Makefile.sources b/src/Makefile.sources index 842aec2..534feec 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -57,12 +57,14 @@ cairo_private = \ cairo-atomic-private.h \ cairo-cache-private.h \ cairo-clip-private.h \ + cairo-combsort-private.h \ cairo-compiler-private.h \ cairo-fixed-private.h \ cairo-fixed-type-private.h \ cairo-freelist-private.h \ cairo-gstate-private.h \ cairo-hash-private.h \ + cairo-image-info-private.h \ cairo-malloc-private.h \ cairo-meta-surface-private.h \ cairo-mutex-impl-private.h \ @@ -76,9 +78,9 @@ cairo_private = \ cairo-path-private.h \ cairo-private.h \ cairo-reference-count-private.h \ - cairo-region-private.h \ cairo-scaled-font-private.h \ cairo-skiplist-private.h \ + cairo-spans-private.h \ cairo-surface-fallback-private.h \ cairo-surface-private.h \ cairo-types-private.h \ @@ -107,6 +109,7 @@ cairo_sources = \ cairo-gstate.c \ cairo-hash.c \ cairo-hull.c \ + cairo-image-info.c \ cairo-image-surface.c \ cairo-lzw.c \ cairo-matrix.c \ @@ -119,6 +122,7 @@ cairo_sources = \ cairo-path.c \ cairo-path-fill.c \ cairo-path-fixed.c \ + cairo-path-in-fill.c \ cairo-path-stroke.c \ cairo-pattern.c \ cairo-pen.c \ @@ -128,12 +132,15 @@ cairo_sources = \ cairo-scaled-font.c \ cairo-skiplist.c \ cairo-slope.c \ + cairo-spans.c \ cairo-spline.c \ cairo-stroke-style.c \ cairo-surface.c \ cairo-surface-fallback.c \ + cairo-tor-scan-converter.c \ cairo-system.c \ cairo-traps.c \ + cairo-toy-font-face.c \ cairo-unicode.c \ cairo-user-font.c \ cairo-version.c \ @@ -183,11 +190,13 @@ cairo_ft_sources = cairo-ft-font.c # These are private, even though they look like public headers cairo_test_surfaces_private = \ test-fallback-surface.h \ + test-fallback16-surface.h \ test-meta-surface.h \ test-paginated-surface.h \ $(NULL) cairo_test_surfaces_sources = \ test-fallback-surface.c \ + test-fallback16-surface.c \ test-meta-surface.c \ test-paginated-surface.c \ $(NULL) @@ -242,3 +251,6 @@ cairo_glitz_sources = cairo-glitz-surface.c cairo_directfb_headers = cairo-directfb.h cairo_directfb_sources = cairo-directfb-surface.c + +cairo_script_headers = cairo-script.h +cairo_script_sources = cairo-script-surface.c diff --git a/src/Makefile.win32 b/src/Makefile.win32 index ee58929..6fdc395 100644 --- a/src/Makefile.win32 +++ b/src/Makefile.win32 @@ -13,7 +13,7 @@ static: inform $(CFG)/cairo-static.lib dynamic: inform $(CFG)/cairo.dll $(CFG)/cairo.dll: $(OBJECTS) - $(CC) $(MS_MDFLAGS) $(MS_LDFLAGS) -Fe$@ $(PIXMAN_LIBS) $(OBJECTS) -link $(CAIRO_LIBS) + $(CC) $(OPT) $(MS_MDFLAGS) $(MS_LDFLAGS) -Fe$@ $(PIXMAN_LIBS) $(OBJECTS) -link $(CAIRO_LIBS) $(CFG)/cairo-static.lib: $(OBJECTS_STATIC) lib -NOLOGO -OUT:$@ $(PIXMAN_LIBS) $(OBJECTS_STATIC) diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features index ada72c9..bed181f 100644 --- a/src/Makefile.win32.features +++ b/src/Makefile.win32.features @@ -203,6 +203,20 @@ ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1) enabled_cairo_pkgconf += cairo-directfb.pc endif +unsupported_cairo_headers += $(cairo_script_headers) +all_cairo_headers += $(cairo_script_headers) +all_cairo_private += $(cairo_script_private) +all_cairo_sources += $(cairo_script_sources) +ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1) +enabled_cairo_headers += $(cairo_script_headers) +enabled_cairo_private += $(cairo_script_private) +enabled_cairo_sources += $(cairo_script_sources) +endif +all_cairo_pkgconf += cairo-script.pc +ifeq ($(CAIRO_HAS_SCRIPT_SURFACE),1) +enabled_cairo_pkgconf += cairo-script.pc +endif + supported_cairo_headers += $(cairo_ft_headers) all_cairo_headers += $(cairo_ft_headers) all_cairo_private += $(cairo_ft_private) @@ -217,6 +231,20 @@ ifeq ($(CAIRO_HAS_FT_FONT),1) enabled_cairo_pkgconf += cairo-ft.pc endif +supported_cairo_headers += $(cairo_fc_headers) +all_cairo_headers += $(cairo_fc_headers) +all_cairo_private += $(cairo_fc_private) +all_cairo_sources += $(cairo_fc_sources) +ifeq ($(CAIRO_HAS_FC_FONT),1) +enabled_cairo_headers += $(cairo_fc_headers) +enabled_cairo_private += $(cairo_fc_private) +enabled_cairo_sources += $(cairo_fc_sources) +endif +all_cairo_pkgconf += cairo-fc.pc +ifeq ($(CAIRO_HAS_FC_FONT),1) +enabled_cairo_pkgconf += cairo-fc.pc +endif + supported_cairo_headers += $(cairo_ps_headers) all_cairo_headers += $(cairo_ps_headers) all_cairo_private += $(cairo_ps_private) @@ -1,7 +1,7 @@ Cairo Library Source Code ========================= -This directory contains the source code to the cairo library. +This directory contains the source code of the cairo library. Source Code Listing @@ -60,7 +60,7 @@ be added to the regression test suite to test the new code. Bibliography ------------ -A detailed list of academic publications used in cairo code is availagle +A detailed list of academic publications used in cairo code is available in the file $(top_srcdir)/BIBLIOGRAPHY. Feel free to update as you implement more papers. diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index 057be52..c767d07 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -38,7 +38,6 @@ #include "cairo-analysis-surface-private.h" #include "cairo-paginated-private.h" -#include "cairo-region-private.h" #include "cairo-meta-surface-private.h" typedef struct { @@ -96,10 +95,10 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, static cairo_int_status_t _analyze_meta_surface_pattern (cairo_analysis_surface_t *surface, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { cairo_surface_t *analysis = &surface->base; - cairo_surface_pattern_t *surface_pattern; + const cairo_surface_pattern_t *surface_pattern; cairo_status_t status; cairo_bool_t old_has_ctm; cairo_matrix_t old_ctm, p2d; @@ -109,14 +108,14 @@ _analyze_meta_surface_pattern (cairo_analysis_surface_t *surface, int old_height; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); - surface_pattern = (cairo_surface_pattern_t *) pattern; + surface_pattern = (const cairo_surface_pattern_t *) pattern; assert (_cairo_surface_is_meta (surface_pattern->surface)); old_width = surface->width; old_height = surface->height; old_clip = surface->current_clip; status = _cairo_surface_get_extents (surface_pattern->surface, &meta_extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; surface->width = meta_extents.width; @@ -215,7 +214,7 @@ _add_operation (cairo_analysis_surface_t *surface, * region there is no benefit in emitting a native operation as * the fallback image will be painted on top. */ - if (_cairo_region_contains_rectangle (&surface->fallback_region, rect) == PIXMAN_REGION_IN) + if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN) return CAIRO_INT_STATUS_IMAGE_FALLBACK; if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { @@ -226,7 +225,7 @@ _add_operation (cairo_analysis_surface_t *surface, * natively supported and the backend will blend the * transparency into the white background. */ - if (_cairo_region_contains_rectangle (&surface->supported_region, rect) == PIXMAN_REGION_OUT) + if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) backend_status = CAIRO_STATUS_SUCCESS; } @@ -235,9 +234,7 @@ _add_operation (cairo_analysis_surface_t *surface, * this region will be emitted as native operations. */ surface->has_supported = TRUE; - status = _cairo_region_union_rect (&surface->supported_region, - &surface->supported_region, - rect); + status = cairo_region_union_rectangle (&surface->supported_region, rect); return status; } @@ -246,9 +243,7 @@ _add_operation (cairo_analysis_surface_t *surface, * emitted. */ surface->has_unsupported = TRUE; - status = _cairo_region_union_rect (&surface->fallback_region, - &surface->fallback_region, - rect); + status = cairo_region_union_rectangle (&surface->fallback_region, rect); /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate * unsupported operations to the meta surface as using @@ -283,9 +278,6 @@ _cairo_analysis_surface_intersect_clip_path (void *abstract_surface, cairo_antialias_t antialias) { cairo_analysis_surface_t *surface = abstract_surface; - double x1, y1, x2, y2; - cairo_rectangle_int_t extent; - cairo_bool_t is_empty; if (path == NULL) { surface->current_clip.x = 0; @@ -293,18 +285,12 @@ _cairo_analysis_surface_intersect_clip_path (void *abstract_surface, surface->current_clip.width = surface->width; surface->current_clip.height = surface->height; } else { - cairo_status_t status; - - status = _cairo_path_fixed_bounds (path, &x1, &y1, &x2, &y2, tolerance); - if (status) - return status; + cairo_rectangle_int_t extents; + cairo_bool_t is_empty; - extent.x = floor (x1); - extent.y = floor (y1); - extent.width = ceil (x2) - extent.x; - extent.height = ceil (y2) - extent.y; - - is_empty = _cairo_rectangle_intersect (&surface->current_clip, &extent); + _cairo_path_fixed_approximate_clip_extents (path, &extents); + is_empty = _cairo_rectangle_intersect (&surface->current_clip, + &extents); } return CAIRO_STATUS_SUCCESS; @@ -321,8 +307,9 @@ _cairo_analysis_surface_get_extents (void *abstract_surface, static cairo_int_status_t _cairo_analysis_surface_paint (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source) + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_rectangle_int_t *paint_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; @@ -333,26 +320,28 @@ _cairo_analysis_surface_paint (void *abstract_surface, backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else backend_status = (*surface->target->backend->paint) (surface->target, op, - source); + source, NULL); if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) backend_status = _analyze_meta_surface_pattern (surface, source); status = _cairo_surface_get_extents (&surface->base, &extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); } is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); + if (paint_extents) + *paint_extents = extents; status = _add_operation (surface, &extents, backend_status); @@ -362,8 +351,9 @@ _cairo_analysis_surface_paint (void *abstract_surface, static cairo_int_status_t _cairo_analysis_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *mask_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t status, backend_status; @@ -374,14 +364,14 @@ _cairo_analysis_surface_mask (void *abstract_surface, backend_status = CAIRO_INT_STATUS_UNSUPPORTED; else backend_status = (*surface->target->backend->mask) (surface->target, op, - source, mask); + source, mask, NULL); if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source; if (_cairo_surface_is_meta (surface_pattern->surface)) { backend_source_status = _analyze_meta_surface_pattern (surface, source); @@ -406,13 +396,14 @@ _cairo_analysis_surface_mask (void *abstract_surface, } status = _cairo_surface_get_extents (&surface->base, &extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; + status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); @@ -422,13 +413,15 @@ _cairo_analysis_surface_mask (void *abstract_surface, cairo_rectangle_int_t mask_extents; status = _cairo_pattern_get_extents (mask, &mask_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); + if (mask_extents) + *mask_extents = extents; status = _add_operation (surface, &extents, backend_status); @@ -438,17 +431,17 @@ _cairo_analysis_surface_mask (void *abstract_surface, static cairo_int_status_t _cairo_analysis_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *stroke_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; - cairo_traps_t traps; cairo_rectangle_int_t extents; cairo_bool_t is_empty; @@ -458,20 +451,20 @@ _cairo_analysis_surface_stroke (void *abstract_surface, backend_status = (*surface->target->backend->stroke) (surface->target, op, source, path, style, ctm, ctm_inverse, - tolerance, antialias); + tolerance, antialias, NULL); if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) backend_status = _analyze_meta_surface_pattern (surface, source); status = _cairo_surface_get_extents (&surface->base, &extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); @@ -480,27 +473,16 @@ _cairo_analysis_surface_stroke (void *abstract_surface, is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); if (_cairo_operator_bounded_by_mask (op)) { - cairo_box_t box; - - _cairo_box_from_rectangle (&box, &extents); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &box); - status = _cairo_path_fixed_stroke_to_traps (path, - style, - ctm, ctm_inverse, - tolerance, - &traps); - if (status) { - _cairo_traps_fini (&traps); - return status; - } + cairo_rectangle_int_t mask_extents; - _cairo_traps_extents (&traps, &box); - _cairo_traps_fini (&traps); + _cairo_path_fixed_approximate_stroke_extents (path, + style, ctm, + &mask_extents); - _cairo_box_round_to_rectangle (&box, &extents); + is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } + if (stroke_extents) + *stroke_extents = extents; status = _add_operation (surface, &extents, backend_status); @@ -510,15 +492,15 @@ _cairo_analysis_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_analysis_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *fill_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; - cairo_traps_t traps; cairo_rectangle_int_t extents; cairo_bool_t is_empty; @@ -527,20 +509,20 @@ _cairo_analysis_surface_fill (void *abstract_surface, else backend_status = (*surface->target->backend->fill) (surface->target, op, source, path, fill_rule, - tolerance, antialias); + tolerance, antialias, NULL); if (backend_status == CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN) backend_status = _analyze_meta_surface_pattern (surface, source); status = _cairo_surface_get_extents (&surface->base, &extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); @@ -549,26 +531,15 @@ _cairo_analysis_surface_fill (void *abstract_surface, is_empty = _cairo_rectangle_intersect (&extents, &surface->current_clip); if (_cairo_operator_bounded_by_mask (op)) { - cairo_box_t box; - - _cairo_box_from_rectangle (&box, &extents); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &box); - status = _cairo_path_fixed_fill_to_traps (path, - fill_rule, - tolerance, - &traps); - if (status) { - _cairo_traps_fini (&traps); - return status; - } + cairo_rectangle_int_t mask_extents; - _cairo_traps_extents (&traps, &box); - _cairo_traps_fini (&traps); + _cairo_path_fixed_approximate_fill_extents (path, + &mask_extents); - _cairo_box_round_to_rectangle (&box, &extents); + is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); } + if (fill_extents) + *fill_extents = extents; status = _add_operation (surface, &extents, backend_status); @@ -578,11 +549,12 @@ _cairo_analysis_surface_fill (void *abstract_surface, static cairo_int_status_t _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *show_glyphs_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; @@ -595,7 +567,7 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, source, glyphs, num_glyphs, scaled_font, - remaining_glyphs); + remaining_glyphs, NULL); else if (surface->target->backend->show_text_glyphs) backend_status = surface->target->backend->show_text_glyphs (surface->target, op, source, @@ -603,7 +575,7 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, glyphs, num_glyphs, NULL, 0, FALSE, - scaled_font); + scaled_font, NULL); else backend_status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -611,14 +583,14 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, backend_status = _analyze_meta_surface_pattern (surface, source); status = _cairo_surface_get_extents (&surface->base, &extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); @@ -631,11 +603,13 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, glyphs, num_glyphs, &glyph_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); } + if (show_glyphs_extents) + *show_glyphs_extents = extents; status = _add_operation (surface, &extents, backend_status); @@ -653,7 +627,7 @@ _cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface) static cairo_int_status_t _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -661,7 +635,8 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *show_text_glyphs_extents) { cairo_analysis_surface_t *surface = abstract_surface; cairo_status_t status, backend_status; @@ -676,14 +651,14 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - scaled_font); + scaled_font, NULL); if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs) { int remaining_glyphs = num_glyphs; backend_status = surface->target->backend->show_glyphs (surface->target, op, source, glyphs, num_glyphs, scaled_font, - &remaining_glyphs); + &remaining_glyphs, NULL); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (remaining_glyphs == 0) @@ -694,14 +669,14 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, backend_status = _analyze_meta_surface_pattern (surface, source); status = _cairo_surface_get_extents (&surface->base, &extents); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &source_extents); @@ -714,11 +689,13 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, glyphs, num_glyphs, &glyph_extents); - if (status) + if (unlikely (status)) return status; is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); } + if (show_text_glyphs_extents) + *show_text_glyphs_extents = extents; status = _add_operation (surface, &extents, backend_status); @@ -737,6 +714,8 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ @@ -758,6 +737,7 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ _cairo_analysis_surface_has_show_text_glyphs, _cairo_analysis_surface_show_text_glyphs }; @@ -771,11 +751,11 @@ _cairo_analysis_surface_create (cairo_surface_t *target, cairo_status_t status; status = target->status; - if (status) + if (unlikely (status)) return _cairo_surface_create_in_error (status); surface = malloc (sizeof (cairo_analysis_surface_t)); - if (surface == NULL) + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); /* I believe the content type here is truly arbitrary. I'm quite @@ -793,14 +773,14 @@ _cairo_analysis_surface_create (cairo_surface_t *target, surface->has_supported = FALSE; surface->has_unsupported = FALSE; + _cairo_region_init (&surface->supported_region); + _cairo_region_init (&surface->fallback_region); + surface->page_bbox.p1.x = 0; surface->page_bbox.p1.y = 0; surface->page_bbox.p2.x = 0; surface->page_bbox.p2.y = 0; - _cairo_region_init (&surface->supported_region); - _cairo_region_init (&surface->fallback_region); - if (width == -1 && height == -1) { surface->current_clip.x = CAIRO_RECT_INT_MIN; surface->current_clip.y = CAIRO_RECT_INT_MIN; @@ -897,42 +877,47 @@ typedef cairo_int_status_t typedef cairo_int_status_t (*_paint_func) (void *surface, cairo_operator_t op, - cairo_pattern_t *source); + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents); typedef cairo_int_status_t (*_mask_func) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask); + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents); typedef cairo_int_status_t (*_stroke_func) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents); typedef cairo_int_status_t (*_fill_func) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias); + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents); typedef cairo_int_status_t (*_show_glyphs_func) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs); + int *remaining_glyphs, + cairo_rectangle_int_t *extents); static const cairo_surface_backend_t cairo_null_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, @@ -947,6 +932,8 @@ static const cairo_surface_backend_t cairo_null_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ (_set_clip_region_func) _return_success, /* set_clip_region */ @@ -968,6 +955,7 @@ static const cairo_surface_backend_t cairo_null_surface_backend = { NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ NULL, /* has_show_text_glyphs */ NULL /* show_text_glyphs */ }; @@ -978,7 +966,7 @@ _cairo_null_surface_create (cairo_content_t content) cairo_surface_t *surface; surface = malloc (sizeof (cairo_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } diff --git a/src/cairo-array.c b/src/cairo-array.c index b2790c8..fcd1246 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -125,6 +125,9 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) if (required_size > INT_MAX || required_size < array->num_elements) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (required_size <= old_size) return CAIRO_STATUS_SUCCESS; @@ -138,7 +141,7 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) if (array->elements == NULL) { array->elements = malloc (sizeof (char *)); - if (array->elements == NULL) + if (unlikely (array->elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); *array->elements = NULL; @@ -148,7 +151,7 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) new_elements = _cairo_realloc_ab (*array->elements, array->size, array->element_size); - if (new_elements == NULL) { + if (unlikely (new_elements == NULL)) { array->size = old_size; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -279,7 +282,7 @@ _cairo_array_append_multiple (cairo_array_t *array, assert (! array->is_snapshot); status = _cairo_array_allocate (array, num_elements, &dest); - if (status) + if (unlikely (status)) return status; memcpy (dest, elements, num_elements * array->element_size); @@ -310,7 +313,7 @@ _cairo_array_allocate (cairo_array_t *array, assert (! array->is_snapshot); status = _cairo_array_grow_by (array, num_elements); - if (status) + if (unlikely (status)) return status; assert (array->num_elements + num_elements <= array->size); @@ -349,14 +352,6 @@ _cairo_array_size (cairo_array_t *array) return array->size; } -/* #cairo_user_data_array_t */ - -typedef struct { - const cairo_user_data_key_t *key; - void *user_data; - cairo_destroy_func_t destroy; -} cairo_user_data_slot_t; - /** * _cairo_user_data_array_init: * @array: a #cairo_user_data_array_t @@ -382,14 +377,18 @@ _cairo_user_data_array_init (cairo_user_data_array_t *array) void _cairo_user_data_array_fini (cairo_user_data_array_t *array) { - int i, num_slots; - cairo_user_data_slot_t *slots; + unsigned int num_slots; num_slots = array->num_elements; - slots = _cairo_array_index (array, 0); - for (i = 0; i < num_slots; i++) { - if (slots[i].user_data != NULL && slots[i].destroy != NULL) - slots[i].destroy (slots[i].user_data); + if (num_slots) { + cairo_user_data_slot_t *slots; + + slots = _cairo_array_index (array, 0); + do { + if (slots->user_data != NULL && slots->destroy != NULL) + slots->destroy (slots->user_data); + slots++; + } while (--num_slots); } _cairo_array_fini (array); @@ -485,8 +484,44 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, } status = _cairo_array_append (array, &new_slot); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } + +cairo_status_t +_cairo_user_data_array_copy (cairo_user_data_array_t *dst, + cairo_user_data_array_t *src) +{ + /* discard any existing user-data */ + if (dst->num_elements != 0) { + _cairo_user_data_array_fini (dst); + _cairo_user_data_array_init (dst); + } + + if (src->num_elements == 0) + return CAIRO_STATUS_SUCCESS; + + return _cairo_array_append_multiple (dst, + _cairo_array_index (src, 0), + src->num_elements); +} + +void +_cairo_user_data_array_foreach (cairo_user_data_array_t *array, + void (*func) (const void *key, + void *elt, + void *closure), + void *closure) +{ + cairo_user_data_slot_t *slots; + int i, num_slots; + + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].user_data != NULL) + func (slots[i].key, slots[i].user_data, closure); + } +} diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h index 2914a02..108cb39 100644 --- a/src/cairo-atomic-private.h +++ b/src/cairo-atomic-private.h @@ -54,6 +54,7 @@ typedef int cairo_atomic_int_t; # define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1)) # define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) #endif @@ -71,6 +72,9 @@ _cairo_atomic_int_dec_and_test (int *x); cairo_private int _cairo_atomic_int_cmpxchg (int *x, int oldv, int newv); +cairo_private void * +_cairo_atomic_ptr_cmpxchg (void **x, void *oldv, void *newv); + #endif @@ -91,6 +95,9 @@ _cairo_atomic_int_set (int *x, int value); #endif +#define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x) +#define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg((int *)x, oldv, newv) #define _cairo_status_set_error(status, err) do { \ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ diff --git a/src/cairo-atomic.c b/src/cairo-atomic.c index c569000..777ba5c 100644 --- a/src/cairo-atomic.c +++ b/src/cairo-atomic.c @@ -71,6 +71,19 @@ _cairo_atomic_int_cmpxchg (int *x, int oldv, int newv) return ret; } +void * +_cairo_atomic_ptr_cmpxchg (void **x, void *oldv, void *newv) +{ + void *ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + if (ret == oldv) + *x = newv; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} #endif #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER diff --git a/src/cairo-base85-stream.c b/src/cairo-base85-stream.c index 8897403..9d42ef4 100644 --- a/src/cairo-base85-stream.c +++ b/src/cairo-base85-stream.c @@ -117,13 +117,14 @@ _cairo_base85_stream_create (cairo_output_stream_t *output) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (cairo_base85_stream_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _cairo_base85_stream_write, + NULL, _cairo_base85_stream_close); stream->output = output; stream->pending = 0; diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c index c657dd9..1d59d70 100644 --- a/src/cairo-bentley-ottmann.c +++ b/src/cairo-bentley-ottmann.c @@ -38,6 +38,7 @@ #include "cairo-skiplist-private.h" #include "cairo-freelist-private.h" +#include "cairo-combsort-private.h" #define DEBUG_VALIDATE 0 #define DEBUG_PRINT_STATE 0 @@ -86,7 +87,7 @@ struct _cairo_bo_edge { cairo_bo_point32_t top; cairo_bo_point32_t middle; cairo_bo_point32_t bottom; - cairo_bool_t reversed; + int dir; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; cairo_bo_trap_t *deferred_trap; @@ -128,8 +129,6 @@ typedef struct _cairo_bo_event_queue { cairo_bo_event_t *startstop_events; cairo_bo_event_t **sorted_startstop_event_ptrs; - unsigned next_startstop_event_index; - unsigned num_startstop_events; } cairo_bo_event_queue_t; /* This structure extends #cairo_skip_list_t, which must come first. */ @@ -197,11 +196,21 @@ _slope_compare (cairo_bo_edge_t *a, int32_t bdx = b->bottom.x - b->top.x; /* Since the dy's are all positive by construction we can fast - * path the case where the two edges point in different directions - * with respect to x. */ - if ((adx ^ bdx) < 0) { - return adx < 0 ? -1 : +1; - } else { + * path several common cases. + */ + + /* First check for vertical lines. */ + if (adx == 0) + return -bdx; + if (bdx == 0) + return adx; + + /* Then where the two edges point in different directions wrt x. */ + if ((adx ^ bdx) < 0) + return adx; + + /* Finally we actually need to do the general comparison. */ + { int32_t ady = a->bottom.y - a->top.y; int32_t bdy = b->bottom.y - b->top.y; cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); @@ -219,16 +228,18 @@ _slope_compare (cairo_bo_edge_t *a, * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: - * A_x + (Y - A_y) * A_dx / A_dy -?- B_x + (Y - B_y) * B_dx / B_dy, - * where -?- is our inequality operator. + * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, + * where ∘ is our inequality operator. * * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are * all positive, so we can rearrange it thus without causing a sign change: - * A_dy * B_dy * (A_x - B_x) -?- (Y - B_y) * B_dx * A_dy + * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy * - (Y - A_y) * A_dx * B_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute - * this comparison directly using 128 bit arithmetic. + * this comparison directly using 128 bit arithmetic. For certain, but common, + * input we can reduce this down to a single 32 bit compare by inspecting the + * deltas. * * (And put the burden of the work on developing fast 128 bit ops, which are * required throughout the tessellator.) @@ -245,32 +256,95 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a, * should prevent that before the tessellation algorithm * begins. */ + int32_t dx; int32_t adx, ady; int32_t bdx, bdy; - cairo_int128_t L, R; + enum { + HAVE_NONE = 0x0, + HAVE_DX = 0x1, + HAVE_ADX = 0x2, + HAVE_DX_ADX = HAVE_DX | HAVE_ADX, + HAVE_BDX = 0x4, + HAVE_DX_BDX = HAVE_DX | HAVE_BDX, + HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, + HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX + } have_dx_adx_bdx = HAVE_ALL; - adx = a->bottom.x - a->top.x; ady = a->bottom.y - a->top.y; + adx = a->bottom.x - a->top.x; + if (adx == 0) + have_dx_adx_bdx &= ~HAVE_ADX; - bdx = b->bottom.x - b->top.x; bdy = b->bottom.y - b->top.y; + bdx = b->bottom.x - b->top.x; + if (bdx == 0) + have_dx_adx_bdx &= ~HAVE_BDX; - L = _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), - a->top.x - b->top.x); + dx = a->top.x - b->top.x; + if (dx == 0) + have_dx_adx_bdx &= ~HAVE_DX; - R = _cairo_int128_sub (_cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, - ady), - y - b->top.y), - _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, - bdy), - y - a->top.y)); +#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) +#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->top.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->top.y) + switch (have_dx_adx_bdx) { + default: + case HAVE_NONE: + return 0; + case HAVE_DX: + /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ + return dx; /* ady * bdy is positive definite */ + case HAVE_ADX: + /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ + return adx; /* bdy * (y - a->top.y) is positive definite */ + case HAVE_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy */ + return -bdx; /* ady * (y - b->top.y) is positive definite */ + case HAVE_ADX_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ + if ((adx ^ bdx) < 0) { + return adx; + } else if (a->top.y == b->top.y) { /* common origin */ + cairo_int64_t adx_bdy, bdx_ady; + + /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ + + adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } else + return _cairo_int128_cmp (A, B); + case HAVE_DX_ADX: + /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ + if ((-adx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t ady_dx, dy_adx; - /* return _cairo_int128_cmp (L, R); */ - if (_cairo_int128_lt (L, R)) - return -1; - if (_cairo_int128_gt (L, R)) - return 1; - return 0; + ady_dx = _cairo_int32x32_64_mul (ady, dx); + dy_adx = _cairo_int32x32_64_mul (a->top.y - y, adx); + + return _cairo_int64_cmp (ady_dx, dy_adx); + } + case HAVE_DX_BDX: + /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ + if ((bdx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t bdy_dx, dy_bdx; + + bdy_dx = _cairo_int32x32_64_mul (bdy, dx); + dy_bdx = _cairo_int32x32_64_mul (y - b->top.y, bdx); + + return _cairo_int64_cmp (bdy_dx, dy_bdx); + } + case HAVE_ALL: + return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); + } +#undef B +#undef A +#undef L } /* @@ -281,12 +355,12 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a, * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: - * A_x + (Y - A_y) * A_dx / A_dy -?- X - * where -?- is our inequality operator. + * A_x + (Y - A_y) * A_dx / A_dy ∘ X + * where ∘ is our inequality operator. * * By construction, we know that A_dy (and (Y - A_y)) are * all positive, so we can rearrange it thus without causing a sign change: - * (Y - A_y) * A_dx -?- (X - A_x) * A_dy + * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 64 bit arithmetic. @@ -304,10 +378,15 @@ edge_compare_for_y_against_x (const cairo_bo_edge_t *a, cairo_int64_t L, R; adx = a->bottom.x - a->top.x; - ady = a->bottom.y - a->top.y; + dx = x - a->top.x; + + if (adx == 0) + return -dx; + if ((adx ^ dx) < 0) + return adx; dy = y - a->top.y; - dx = x - a->top.x; + ady = a->bottom.y - a->top.y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); @@ -352,7 +431,7 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a, case HAVE_NEITHER: return edges_compare_x_for_y_general (a, b, y); case HAVE_AX: - return - edge_compare_for_y_against_x (b, y, ax); + return -edge_compare_for_y_against_x (b, y, ax); case HAVE_BX: return edge_compare_for_y_against_x (a, y, bx); case HAVE_BOTH: @@ -550,21 +629,18 @@ cairo_bo_event_compare_abstract (void *list, } static int -cairo_bo_event_compare_pointers (void const *voida, - void const *voidb) +cairo_bo_event_compare_pointers (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) { - cairo_bo_event_t const * const *a = voida; - cairo_bo_event_t const * const *b = voidb; - if (*a != *b) { - int cmp = cairo_bo_event_compare (*a, *b); - if (cmp) - return cmp; - if (*a < *b) - return -1; - if (*a > *b) - return +1; - } - return 0; + int cmp; + + if (a == b) + return 0; + cmp = cairo_bo_event_compare (a, b); + if (cmp) + return cmp; + + return a - b; } static inline cairo_int64_t @@ -624,12 +700,61 @@ intersect_lines (cairo_bo_edge_t *a, int32_t dx2 = b->top.x - b->bottom.x; int32_t dy2 = b->top.y - b->bottom.y; - cairo_int64_t den_det = det32_64 (dx1, dy1, dx2, dy2); + cairo_int64_t den_det; + cairo_int64_t R; cairo_quorem64_t qr; + den_det = det32_64 (dx1, dy1, dx2, dy2); if (_cairo_int64_is_zero (den_det)) return CAIRO_BO_STATUS_PARALLEL; + /* Q: Can we determine that the lines do not intersect (within range) + * much more cheaply than computing the intersection point i.e. by + * avoiding the division? + * + * X = ax + t * adx = bx + s * bdx; + * Y = ay + t * ady = by + s * bdy; + * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) + * => t * L = R + * + * Therefore we can reject any intersection (under the criteria for + * valid intersection events) if: + * L^R < 0 => t < 0, or + * L<R => t > 1 + * + * (where top/bottom must at least extend to the line endpoints). + * + * A similar substitution can be performed for s, yielding: + * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) + */ + R = det32_64 (dx2, dy2, b->top.x - a->top.x, b->top.y - a->top.y); + if (_cairo_int64_is_zero (R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + } else { + if (_cairo_int64_le (den_det, R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + } + + R = det32_64 (dy1, dx1, a->top.y - b->top.y, a->top.x - b->top.x); + if (_cairo_int64_is_zero (R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + } else { + if (_cairo_int64_le (den_det, R)) + return CAIRO_BO_STATUS_NO_INTERSECTION; + } + + /* We now know that the two lines should intersect within range. */ + a_det = det32_64 (a->top.x, a->top.y, a->bottom.x, a->bottom.y); b_det = det32_64 (b->top.x, b->top.y, @@ -815,19 +940,24 @@ _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) cairo_bo_event_t *intersection = elt ? SKIP_ELT_TO_EVENT (elt) : NULL; cairo_bo_event_t *startstop; - if (event_queue->next_startstop_event_index == event_queue->num_startstop_events) + startstop = *event_queue->sorted_startstop_event_ptrs; + if (startstop == NULL) return intersection; - startstop = event_queue->sorted_startstop_event_ptrs[ - event_queue->next_startstop_event_index]; - if (!intersection || cairo_bo_event_compare (startstop, intersection) <= 0) + if (intersection == NULL || + cairo_bo_event_compare (startstop, intersection) <= 0) { - event_queue->next_startstop_event_index++; + event_queue->sorted_startstop_event_ptrs++; return startstop; } + return intersection; } +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare_pointers) + static cairo_status_t _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *edges, @@ -837,31 +967,24 @@ _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, cairo_bo_event_t *events, **sorted_event_ptrs; unsigned num_events = 2*num_edges; - memset (event_queue, 0, sizeof(*event_queue)); - - _cairo_skip_list_init (&event_queue->intersection_queue, - cairo_bo_event_compare_abstract, - sizeof (cairo_bo_event_t)); - if (0 == num_edges) - return CAIRO_STATUS_SUCCESS; - /* The skip_elt_t field of a cairo_bo_event_t isn't used for start * or stop events, so this allocation is safe. XXX: make the * event type a union so it doesn't always contain the skip * elt? */ - events = _cairo_malloc_ab (num_events, sizeof (cairo_bo_event_t) + sizeof(cairo_bo_event_t*)); - if (events == NULL) + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_event_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); sorted_event_ptrs = (cairo_bo_event_t **) (events + num_events); event_queue->startstop_events = events; event_queue->sorted_startstop_event_ptrs = sorted_event_ptrs; - event_queue->num_startstop_events = num_events; - event_queue->next_startstop_event_index = 0; for (i = 0; i < num_edges; i++) { - sorted_event_ptrs[2*i] = &events[2*i]; - sorted_event_ptrs[2*i+1] = &events[2*i+1]; + sorted_event_ptrs[i] = &events[2*i]; + sorted_event_ptrs[i+num_edges] = &events[2*i+1]; /* Initialize "middle" to top */ edges[i].middle = edges[i].top; @@ -877,9 +1000,13 @@ _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, edges[i].bottom); } - qsort (sorted_event_ptrs, num_events, - sizeof(cairo_bo_event_t *), - cairo_bo_event_compare_pointers); + _cairo_bo_event_queue_sort (sorted_event_ptrs, num_events); + event_queue->sorted_startstop_event_ptrs[num_events] = NULL; + + _cairo_skip_list_init (&event_queue->intersection_queue, + cairo_bo_event_compare_abstract, + sizeof (cairo_bo_event_t)); + return CAIRO_STATUS_SUCCESS; } @@ -930,8 +1057,9 @@ static void _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) { _cairo_skip_list_init (&sweep_line->active_edges, - _sweep_line_elt_compare, - sizeof (sweep_line_elt_t)); + _sweep_line_elt_compare, + sizeof (sweep_line_elt_t)); + sweep_line->head = NULL; sweep_line->tail = NULL; sweep_line->current_y = 0; @@ -952,8 +1080,8 @@ _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t **prev_of_next, **next_of_prev; sweep_line_elt = _cairo_skip_list_insert (&sweep_line->active_edges, &edge, - 1 /* unique inserts*/); - if (sweep_line_elt == NULL) + 1 /* unique inserts*/); + if (unlikely (sweep_line_elt == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); next_elt = sweep_line_elt->elt.next[0]; @@ -983,7 +1111,8 @@ _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, { cairo_bo_edge_t **left_next, **right_prev; - _cairo_skip_list_delete_given (&sweep_line->active_edges, &edge->sweep_line_elt->elt); + _cairo_skip_list_delete_given (&sweep_line->active_edges, + &edge->sweep_line_elt->elt); left_next = &sweep_line->head; if (edge->prev) @@ -1312,10 +1441,7 @@ _active_edges_to_traps (cairo_bo_edge_t *head, for (edge = head; edge; edge = edge->next) { if (fill_rule == CAIRO_FILL_RULE_WINDING) { - if (edge->reversed) - in_out++; - else - in_out--; + in_out += edge->dir; if (in_out == 0) { status = _cairo_bo_edge_end_trap (edge, top, bo_traps); if (status) @@ -1364,11 +1490,15 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges, cairo_bo_edge_t *left, *right; cairo_bo_edge_t *edge1, *edge2; + if (num_edges == 0) + return CAIRO_STATUS_SUCCESS; + status = _cairo_bo_event_queue_init (&event_queue, edges, num_edges); if (status) return status; _cairo_bo_sweep_line_init (&sweep_line); + _cairo_bo_traps_init (&bo_traps, traps, xmin, ymin, xmax, ymax); #if DEBUG_PRINT_STATE @@ -1530,13 +1660,15 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, if (0 == polygon->num_edges) return CAIRO_STATUS_SUCCESS; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + has_limits = _cairo_traps_get_limit (traps, &limit); - if (polygon->num_edges < ARRAY_LENGTH (stack_edges)) { - edges = stack_edges; - } else { + edges = stack_edges; + if (polygon->num_edges > ARRAY_LENGTH (stack_edges)) { edges = _cairo_malloc_ab (polygon->num_edges, sizeof (cairo_bo_edge_t)); - if (edges == NULL) + if (unlikely (edges == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1604,10 +1736,7 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, edge->top.y = top.y; edge->bottom.x = bot.x; edge->bottom.y = bot.y; - /* XXX: The 'clockWise' name that cairo_polygon_t uses is - * totally bogus. It's really a (negated!) description of - * whether the edge is reversed. */ - edge->reversed = (! polygon->edges[i].clockWise); + edge->dir = polygon->edges[i].dir; edge->deferred_trap = NULL; edge->prev = NULL; edge->next = NULL; diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h index 4ae63ad..49ff69c 100644 --- a/src/cairo-cache-private.h +++ b/src/cairo-cache-private.h @@ -88,6 +88,20 @@ typedef struct _cairo_cache_entry { unsigned long size; } cairo_cache_entry_t; +typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry); + +struct _cairo_cache { + cairo_hash_table_t *hash_table; + + cairo_cache_predicate_func_t predicate; + cairo_destroy_func_t entry_destroy; + + unsigned long max_size; + unsigned long size; + + int freeze_count; +}; + typedef cairo_bool_t (*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b); @@ -95,13 +109,15 @@ typedef void (*cairo_cache_callback_func_t) (void *entry, void *closure); -cairo_private cairo_cache_t * -_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal, - cairo_destroy_func_t entry_destroy, - unsigned long max_size); +cairo_private cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + cairo_cache_keys_equal_func_t keys_equal, + cairo_cache_predicate_func_t predicate, + cairo_destroy_func_t entry_destroy, + unsigned long max_size); cairo_private void -_cairo_cache_destroy (cairo_cache_t *cache); +_cairo_cache_fini (cairo_cache_t *cache); cairo_private void _cairo_cache_freeze (cairo_cache_t *cache); @@ -109,17 +125,20 @@ _cairo_cache_freeze (cairo_cache_t *cache); cairo_private void _cairo_cache_thaw (cairo_cache_t *cache); -cairo_private cairo_bool_t +cairo_private void * _cairo_cache_lookup (cairo_cache_t *cache, - cairo_cache_entry_t *key, - cairo_cache_entry_t **entry_return); + cairo_cache_entry_t *key); cairo_private cairo_status_t _cairo_cache_insert (cairo_cache_t *cache, cairo_cache_entry_t *entry); cairo_private void -_cairo_cache_foreach (cairo_cache_t *cache, +_cairo_cache_remove (cairo_cache_t *cache, + cairo_cache_entry_t *entry); + +cairo_private void +_cairo_cache_foreach (cairo_cache_t *cache, cairo_cache_callback_func_t cache_callback, void *closure); diff --git a/src/cairo-cache.c b/src/cairo-cache.c index 1c458df..025dd9f 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -39,56 +39,19 @@ #include "cairoint.h" static void -_cairo_cache_remove (cairo_cache_t *cache, - cairo_cache_entry_t *entry); - -static void _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, - unsigned long additional); + unsigned long additional); -static cairo_status_t -_cairo_cache_init (cairo_cache_t *cache, - cairo_cache_keys_equal_func_t keys_equal, - cairo_destroy_func_t entry_destroy, - unsigned long max_size) +static cairo_bool_t +_cairo_cache_entry_is_non_zero (const void *entry) { - cache->hash_table = _cairo_hash_table_create (keys_equal); - if (cache->hash_table == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - cache->entry_destroy = entry_destroy; - - cache->max_size = max_size; - cache->size = 0; - - cache->freeze_count = 0; - - return CAIRO_STATUS_SUCCESS; + return ((const cairo_cache_entry_t *) entry)->size; } -static void -_cairo_cache_fini (cairo_cache_t *cache) -{ - cairo_cache_entry_t *entry; - - /* We have to manually remove all entries from the cache ourselves - * rather than relying on _cairo_hash_table_destroy() to do that - * since otherwise the cache->entry_destroy callback would not get - * called on each entry. */ - - while (1) { - entry = _cairo_hash_table_random_entry (cache->hash_table, NULL); - if (entry == NULL) - break; - _cairo_cache_remove (cache, entry); - } - - _cairo_hash_table_destroy (cache->hash_table); - cache->size = 0; -} /** - * _cairo_cache_create: + * _cairo_cache_init: + * @cache: the #cairo_cache_t to initialise * @keys_equal: a function to return %TRUE if two keys are equal * @entry_destroy: destroy notifier for cache entries * @max_size: the maximum size for this cache @@ -121,44 +84,53 @@ _cairo_cache_fini (cairo_cache_t *cache) * used to establish a window during which no automatic removal of * entries will occur. **/ -cairo_cache_t * -_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal, - cairo_destroy_func_t entry_destroy, - unsigned long max_size) +cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + cairo_cache_keys_equal_func_t keys_equal, + cairo_cache_predicate_func_t predicate, + cairo_destroy_func_t entry_destroy, + unsigned long max_size) { - cairo_status_t status; - cairo_cache_t *cache; + cache->hash_table = _cairo_hash_table_create (keys_equal); + if (unlikely (cache->hash_table == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - cache = malloc (sizeof (cairo_cache_t)); - if (cache == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return NULL; - } + if (predicate == NULL) + predicate = _cairo_cache_entry_is_non_zero; + cache->predicate = predicate; + cache->entry_destroy = entry_destroy; - status = _cairo_cache_init (cache, keys_equal, entry_destroy, max_size); - if (status) { - free (cache); - return NULL; - } + cache->max_size = max_size; + cache->size = 0; + + cache->freeze_count = 0; - return cache; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_cache_pluck (void *entry, void *closure) +{ + _cairo_cache_remove (closure, entry); } /** - * _cairo_cache_destroy: + * _cairo_cache_fini: * @cache: a cache to destroy * * Immediately destroys the given cache, freeing all resources * associated with it. As part of this process, the entry_destroy() - * function, (as passed to _cairo_cache_create()), will be called for + * function, (as passed to _cairo_cache_init()), will be called for * each entry in the cache. **/ void -_cairo_cache_destroy (cairo_cache_t *cache) +_cairo_cache_fini (cairo_cache_t *cache) { - _cairo_cache_fini (cache); - - free (cache); + _cairo_hash_table_foreach (cache->hash_table, + _cairo_cache_pluck, + cache); + assert (cache->size == 0); + _cairo_hash_table_destroy (cache->hash_table); } /** @@ -216,20 +188,18 @@ _cairo_cache_thaw (cairo_cache_t *cache) * * Performs a lookup in @cache looking for an entry which has a key * that matches @key, (as determined by the keys_equal() function - * passed to _cairo_cache_create()). + * passed to _cairo_cache_init()). * * Return value: %TRUE if there is an entry in the cache that matches * @key, (which will now be in *entry_return). %FALSE otherwise, (in * which case *entry_return will be %NULL). **/ -cairo_bool_t +void * _cairo_cache_lookup (cairo_cache_t *cache, - cairo_cache_entry_t *key, - cairo_cache_entry_t **entry_return) + cairo_cache_entry_t *key) { return _cairo_hash_table_lookup (cache->hash_table, - (cairo_hash_entry_t *) key, - (cairo_hash_entry_t **) entry_return); + (cairo_hash_entry_t *) key); } /** @@ -246,8 +216,9 @@ _cairo_cache_remove_random (cairo_cache_t *cache) { cairo_cache_entry_t *entry; - entry = _cairo_hash_table_random_entry (cache->hash_table, NULL); - if (entry == NULL) + entry = _cairo_hash_table_random_entry (cache->hash_table, + cache->predicate); + if (unlikely (entry == NULL)) return FALSE; _cairo_cache_remove (cache, entry); @@ -300,7 +271,7 @@ _cairo_cache_insert (cairo_cache_t *cache, status = _cairo_hash_table_insert (cache->hash_table, (cairo_hash_entry_t *) entry); - if (status) + if (unlikely (status)) return status; cache->size += entry->size; @@ -314,13 +285,8 @@ _cairo_cache_insert (cairo_cache_t *cache, * @entry: an entry that exists in the cache * * Remove an existing entry from the cache. - * - * (Note: If any caller wanted access to a non-static version of this - * function, an improved version would require only a key rather than - * an entry. Fixing that would require fixing _cairo_hash_table_remove - * to return (a copy of?) the entry being removed.) **/ -static void +void _cairo_cache_remove (cairo_cache_t *cache, cairo_cache_entry_t *entry) { @@ -343,7 +309,7 @@ _cairo_cache_remove (cairo_cache_t *cache, * non-specified order. **/ void -_cairo_cache_foreach (cairo_cache_t *cache, +_cairo_cache_foreach (cairo_cache_t *cache, cairo_cache_callback_func_t cache_callback, void *closure) { @@ -356,8 +322,20 @@ unsigned long _cairo_hash_string (const char *c) { /* This is the djb2 hash. */ - unsigned long hash = 5381; + unsigned long hash = _CAIRO_HASH_INIT_VALUE; while (c && *c) hash = ((hash << 5) + hash) + *c++; return hash; } + +unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *ptr, + unsigned int length) +{ + const uint8_t *bytes = ptr; + /* This is the djb2 hash. */ + while (length--) + hash = ((hash << 5) + hash) + *bytes++; + return hash; +} diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c index 008cc68..1f4cb92 100644 --- a/src/cairo-cff-subset.c +++ b/src/cairo-cff-subset.c @@ -108,6 +108,7 @@ typedef struct _cairo_cff_font { unsigned char *data_end; cff_header_t *header; char *font_name; + char *ps_name; cairo_hash_table_t *top_dict; cairo_hash_table_t *private_dict; cairo_array_t strings_index; @@ -298,7 +299,7 @@ cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_pt element.is_copy = FALSE; element.data = data + start; status = _cairo_array_append (index, &element); - if (status) + if (unlikely (status)) return status; start = end; } @@ -324,7 +325,7 @@ cff_index_write (cairo_array_t *index, cairo_array_t *output) num_elem = _cairo_array_num_elements (index); count = cpu_to_be16 ((uint16_t) num_elem); status = _cairo_array_append_multiple (output, &count, 2); - if (status) + if (unlikely (status)) return status; if (num_elem == 0) @@ -347,13 +348,13 @@ cff_index_write (cairo_array_t *index, cairo_array_t *output) buf[0] = (unsigned char) offset_size; status = _cairo_array_append (output, buf); - if (status) + if (unlikely (status)) return status; offset = 1; encode_index_offset (buf, offset_size, offset); status = _cairo_array_append_multiple (output, buf, offset_size); - if (status) + if (unlikely (status)) return status; for (i = 0; i < num_elem; i++) { @@ -361,7 +362,7 @@ cff_index_write (cairo_array_t *index, cairo_array_t *output) offset += element->length; encode_index_offset (buf, offset_size, offset); status = _cairo_array_append_multiple (output, buf, offset_size); - if (status) + if (unlikely (status)) return status; } @@ -370,7 +371,7 @@ cff_index_write (cairo_array_t *index, cairo_array_t *output) status = _cairo_array_append_multiple (output, element->data, element->length); - if (status) + if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; @@ -399,13 +400,13 @@ cff_index_append_copy (cairo_array_t *index, element.length = length; element.is_copy = TRUE; element.data = malloc (element.length); - if (element.data == NULL) + if (unlikely (element.data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (element.data, object, element.length); status = _cairo_array_append (index, &element); - if (status) { + if (unlikely (status)) { free (element.data); return status; } @@ -440,8 +441,8 @@ static cairo_status_t cff_dict_init (cairo_hash_table_t **dict) { *dict = _cairo_hash_table_create (_cairo_cff_dict_equal); - if (*dict == NULL) - return CAIRO_STATUS_NO_MEMORY; + if (unlikely (*dict == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } @@ -456,24 +457,24 @@ _cairo_dict_init_key (cff_dict_operator_t *key, int operator) static cairo_status_t cff_dict_create_operator (int operator, unsigned char *operand, - int operand_length, + int size, cff_dict_operator_t **out) { cff_dict_operator_t *op; op = malloc (sizeof (cff_dict_operator_t)); - if (op == NULL) + if (unlikely (op == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_dict_init_key (op, operator); - op->operand = malloc (operand_length); - if (op->operand == NULL) { + op->operand = malloc (size); + if (unlikely (op->operand == NULL)) { free (op); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - memcpy (op->operand, operand, operand_length); - op->operand_length = operand_length; + memcpy (op->operand, operand, size); + op->operand_length = size; op->operand_offset = -1; *out = op; @@ -496,7 +497,7 @@ cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) size = operand_length (p); if (size != 0) { status = _cairo_array_append_multiple (&operands, p, size); - if (status) + if (unlikely (status)) goto fail; p += size; @@ -506,11 +507,11 @@ cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) _cairo_array_index (&operands, 0), _cairo_array_num_elements (&operands), &op); - if (status) + if (unlikely (status)) goto fail; status = _cairo_hash_table_insert (dict, &op->base); - if (status) + if (unlikely (status)) goto fail; _cairo_array_truncate (&operands, 0); @@ -529,9 +530,8 @@ cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator) cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); - if (_cairo_hash_table_lookup (dict, &key.base, - (cairo_hash_entry_t **) &op)) - { + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { free (op->operand); _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op); free (op); @@ -546,9 +546,8 @@ cff_dict_get_operands (cairo_hash_table_t *dict, cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); - if (_cairo_hash_table_lookup (dict, &key.base, - (cairo_hash_entry_t **) &op)) - { + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { *size = op->operand_length; return op->operand; } @@ -566,12 +565,11 @@ cff_dict_set_operands (cairo_hash_table_t *dict, cairo_status_t status; _cairo_dict_init_key (&key, operator); - if (_cairo_hash_table_lookup (dict, &key.base, - (cairo_hash_entry_t **) &op)) - { + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { free (op->operand); op->operand = malloc (size); - if (op->operand == NULL) + if (unlikely (op->operand == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (op->operand, operand, size); @@ -580,11 +578,11 @@ cff_dict_set_operands (cairo_hash_table_t *dict, else { status = cff_dict_create_operator (operator, operand, size, &op); - if (status) + if (unlikely (status)) return status; status = _cairo_hash_table_insert (dict, &op->base); - if (status) + if (unlikely (status)) return status; } @@ -599,9 +597,8 @@ cff_dict_get_location (cairo_hash_table_t *dict, cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); - if (_cairo_hash_table_lookup (dict, &key.base, - (cairo_hash_entry_t **) &op)) - { + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { *size = op->operand_length; return op->operand_offset; } @@ -660,8 +657,8 @@ cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) /* The CFF specification requires that the Top Dict of CID fonts * begin with the ROS operator. */ _cairo_dict_init_key (&key, ROS_OP); - if (_cairo_hash_table_lookup (dict, &key.base, - (cairo_hash_entry_t **) &op)) + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) cairo_dict_write_operator (op, &write_info); _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info); @@ -670,18 +667,19 @@ cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) } static void -cff_dict_fini (cairo_hash_table_t *dict) +_cff_dict_entry_pluck (void *_entry, void *dict) { - cff_dict_operator_t *entry; + cff_dict_operator_t *entry = _entry; - while (1) { - entry = _cairo_hash_table_random_entry (dict, NULL); - if (entry == NULL) - break; - free (entry->operand); - _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) entry); - free (entry); - } + _cairo_hash_table_remove (dict, &entry->base); + free (entry->operand); + free (entry); +} + +static void +cff_dict_fini (cairo_hash_table_t *dict) +{ + _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict); _cairo_hash_table_destroy (dict); } @@ -728,7 +726,7 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t *font, unsigned char *p; status = cff_dict_read (private_dict, ptr, size); - if (status) + if (unlikely (status)) return status; operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i); @@ -736,13 +734,13 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t *font, decode_integer (operand, &offset); p = ptr + offset; status = cff_index_read (local_sub_index, &p, font->data_end); - if (status) + if (unlikely (status)) return status; /* Use maximum sized encoding to reserve space for later modification. */ end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; } @@ -755,7 +753,7 @@ cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) int type, num_ranges, first, last, fd, i, j; font->fdselect = calloc (font->num_glyphs, sizeof (int)); - if (font->fdselect == NULL) + if (unlikely (font->fdselect == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); type = *p++; @@ -797,37 +795,37 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) cff_index_init (&index); status = cff_index_read (&index, &ptr, font->data_end); - if (status) + if (unlikely (status)) goto fail; font->num_fontdicts = _cairo_array_num_elements (&index); font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); - if (font->fd_dict == NULL) { + if (unlikely (font->fd_dict == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); - if (font->fd_private_dict == NULL) { + if (unlikely (font->fd_private_dict == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts); - if (font->fd_local_sub_index == NULL) { + if (unlikely (font->fd_local_sub_index == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } for (i = 0; i < font->num_fontdicts; i++) { status = cff_dict_init (&font->fd_dict[i]); - if (status) + if (unlikely (status)) goto fail; element = _cairo_array_index (&index, i); status = cff_dict_read (font->fd_dict[i], element->data, element->length); - if (status) + if (unlikely (status)) goto fail; operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size); @@ -838,7 +836,7 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) operand = decode_integer (operand, &size); decode_integer (operand, &offset); status = cff_dict_init (&font->fd_private_dict[i]); - if (status) + if (unlikely (status)) goto fail; cff_index_init (&font->fd_local_sub_index[i]); @@ -847,7 +845,7 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) &font->fd_local_sub_index[i], font->data + offset, size); - if (status) + if (unlikely (status)) goto fail; /* Set integer operand to max value to use max size encoding to reserve @@ -855,7 +853,7 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) goto fail; } @@ -882,12 +880,12 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); - if (status) + if (unlikely (status)) goto fail; element = _cairo_array_index (&index, 0); status = cff_dict_read (font->top_dict, element->data, element->length); - if (status) + if (unlikely (status)) goto fail; if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) @@ -899,7 +897,7 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) decode_integer (operand, &offset); p = font->data + offset; status = cff_index_read (&font->charstrings_index, &p, font->data_end); - if (status) + if (unlikely (status)) goto fail; font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); @@ -907,13 +905,13 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size); decode_integer (operand, &offset); status = cairo_cff_font_read_fdselect (font, font->data + offset); - if (status) + if (unlikely (status)) goto fail; operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size); decode_integer (operand, &offset); status = cairo_cff_font_read_cid_fontdict (font, font->data + offset); - if (status) + if (unlikely (status)) goto fail; } else { operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size); @@ -924,7 +922,7 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) &font->local_sub_index, font->data + offset, size); - if (status) + if (unlikely (status)) goto fail; } @@ -932,22 +930,22 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, FDSELECT_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, FDARRAY_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) goto fail; cff_dict_remove (font->top_dict, ENCODING_OP); @@ -995,7 +993,7 @@ cairo_cff_font_read_font (cairo_cff_font_t *font) for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) { status = font_read_funcs[i] (font); - if (status) + if (unlikely (status)) return status; } @@ -1016,26 +1014,26 @@ cairo_cff_font_set_ros_strings (cairo_cff_font_t *font) status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)registry, strlen(registry)); - if (status) + if (unlikely (status)) return status; sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)ordering, strlen(ordering)); - if (status) + if (unlikely (status)) return status; p = encode_integer (buf, sid1); p = encode_integer (p, sid2); p = encode_integer (p, 0); status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf); - if (status) + if (unlikely (status)) return status; p = encode_integer (buf, font->scaled_font_subset->num_glyphs); status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1064,12 +1062,12 @@ cairo_cff_font_subset_dict_string(cairo_cff_font_t *font, element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS); sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append (&font->strings_subset_index, element->data, element->length); - if (status) + if (unlikely (status)) return status; p = encode_integer (buf, sid); status = cff_dict_set_operands (dict, operator, buf, p - buf); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1096,7 +1094,7 @@ cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) { status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]); - if (status) + if (unlikely (status)) return status; } @@ -1116,7 +1114,7 @@ cairo_cff_font_subset_charstrings (cairo_cff_font_t *font) status = cff_index_append (&font->charstrings_subset_index, element->data, element->length); - if (status) + if (unlikely (status)) return status; } @@ -1132,19 +1130,19 @@ cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); - if (font->fdselect_subset == NULL) + if (unlikely (font->fdselect_subset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int)); - if (font->fd_subset_map == NULL) + if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int)); - if (font->private_dict_offset == NULL) + if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); reverse_map = calloc (font->num_fontdicts, sizeof (int)); - if (reverse_map == NULL) + if (unlikely (reverse_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < font->num_fontdicts; i++) @@ -1174,7 +1172,7 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) font->num_fontdicts = 1; font->fd_dict = malloc (sizeof (cairo_hash_table_t *)); - if (font->fd_dict == NULL) + if (unlikely (font->fd_dict == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (cff_dict_init (&font->fd_dict[0])) { @@ -1185,11 +1183,11 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) } font->fd_subset_map = malloc (sizeof (int)); - if (font->fd_subset_map == NULL) + if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->private_dict_offset = malloc (sizeof (int)); - if (font->private_dict_offset == NULL) + if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->fd_subset_map[0] = 0; @@ -1200,7 +1198,7 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1213,17 +1211,17 @@ cairo_cff_font_subset_strings (cairo_cff_font_t *font) unsigned int i; status = cairo_cff_font_subset_dict_strings (font, font->top_dict); - if (status) + if (unlikely (status)) return status; if (font->is_cid) { for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]); - if (status) + if (unlikely (status)) return status; } } else { @@ -1239,22 +1237,22 @@ cairo_cff_font_subset_font (cairo_cff_font_t *font) cairo_status_t status; status = cairo_cff_font_set_ros_strings (font); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_subset_charstrings (font); - if (status) + if (unlikely (status)) return status; if (font->is_cid) status = cairo_cff_font_subset_fontdict (font); else status = cairo_cff_font_create_cid_fontdict (font); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_subset_strings (font); - if (status) + if (unlikely (status)) return status; return status; @@ -1302,11 +1300,11 @@ cairo_cff_font_write_name (cairo_cff_font_t *font) status = cff_index_append_copy (&index, (unsigned char *) font->subset_font_name, strlen(font->subset_font_name)); - if (status) + if (unlikely (status)) goto FAIL; status = cff_index_write (&index, &font->output); - if (status) + if (unlikely (status)) goto FAIL; FAIL: @@ -1330,27 +1328,27 @@ cairo_cff_font_write_top_dict (cairo_cff_font_t *font) count = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &count, 2); - if (status) + if (unlikely (status)) return status; buf[0] = offset_size; status = _cairo_array_append (&font->output, buf); - if (status) + if (unlikely (status)) return status; encode_index_offset (buf, offset_size, 1); status = _cairo_array_append_multiple (&font->output, buf, offset_size); - if (status) + if (unlikely (status)) return status; /* Reserve space for last element of offset array and update after * dict is written */ offset_index = _cairo_array_num_elements (&font->output); status = _cairo_array_append_multiple (&font->output, buf, offset_size); - if (status) + if (unlikely (status)) return status; dict_start = _cairo_array_num_elements (&font->output); status = cff_dict_write (font->top_dict, &font->output); - if (status) + if (unlikely (status)) return status; dict_size = _cairo_array_num_elements (&font->output) - dict_start; @@ -1385,13 +1383,13 @@ cairo_cff_font_write_fdselect (cairo_cff_font_t *font) if (font->is_cid) { data = 0; status = _cairo_array_append (&font->output, &data); - if (status) + if (unlikely (status)) return status; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { data = font->fdselect_subset[i]; status = _cairo_array_append (&font->output, &data); - if (status) + if (unlikely (status)) return status; } } else { @@ -1399,7 +1397,7 @@ cairo_cff_font_write_fdselect (cairo_cff_font_t *font) uint16_t word; status = _cairo_array_grow_by (&font->output, 9); - if (status) + if (unlikely (status)) return status; byte = 3; @@ -1435,7 +1433,7 @@ cairo_cff_font_write_charset (cairo_cff_font_t *font) cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); status = _cairo_array_grow_by (&font->output, 5); - if (status) + if (unlikely (status)) return status; byte = 2; @@ -1474,22 +1472,22 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP); count = cpu_to_be16 (font->num_subset_fontdicts); status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t)); - if (status) + if (unlikely (status)) return status; status = _cairo_array_append (&font->output, &offset_size); - if (status) + if (unlikely (status)) return status; status = _cairo_array_allocate (&font->output, (font->num_subset_fontdicts + 1)*offset_size, (void **) &offset_array); - if (status) + if (unlikely (status)) return status; offset_base = _cairo_array_num_elements (&font->output) - 1; *offset_array++ = cpu_to_be32(1); for (i = 0; i < font->num_subset_fontdicts; i++) { status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]], &font->output); - if (status) + if (unlikely (status)) return status; *offset_array++ = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base); } @@ -1513,7 +1511,7 @@ cairo_cff_font_write_private_dict (cairo_cff_font_t *font, /* Write private dict and update offset and size in top dict */ font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output); status = cff_dict_write (private_dict, &font->output); - if (status) + if (unlikely (status)) return status; size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; @@ -1552,7 +1550,7 @@ cairo_cff_font_write_local_sub (cairo_cff_font_t *font, p = _cairo_array_index (&font->output, offset); memcpy (p, buf, buf_end - buf); status = cff_index_write (local_sub_index, &font->output); - if (status) + if (unlikely (status)) return status; } @@ -1573,7 +1571,7 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) i, font->fd_dict[font->fd_subset_map[i]], font->fd_private_dict[font->fd_subset_map[i]]); - if (status) + if (unlikely (status)) return status; } @@ -1583,7 +1581,7 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) i, font->fd_private_dict[font->fd_subset_map[i]], &font->fd_local_sub_index[font->fd_subset_map[i]]); - if (status) + if (unlikely (status)) return status; } } else { @@ -1591,14 +1589,14 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) 0, font->fd_dict[0], font->private_dict); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_write_local_sub (font, 0, font->private_dict, &font->local_sub_index); - if (status) + if (unlikely (status)) return status; } @@ -1629,7 +1627,7 @@ cairo_cff_font_write_subset (cairo_cff_font_t *font) for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) { status = font_write_funcs[i] (font); - if (status) + if (unlikely (status)) return status; } @@ -1644,15 +1642,15 @@ cairo_cff_font_generate (cairo_cff_font_t *font, cairo_int_status_t status; status = cairo_cff_font_read_font (font); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_subset_font (font); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_write_subset (font); - if (status) + if (unlikely (status)) return status; *data = _cairo_array_index (&font->output, 0); @@ -1678,7 +1676,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char*) &hhea, &size); - if (status) + if (unlikely (status)) return status; num_hmetrics = be16_to_cpu (hhea.num_hmetrics); @@ -1691,7 +1689,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) TT_TAG_hmtx, glyph_index * long_entry_size, buf, &short_entry_size); - if (status) + if (unlikely (status)) return status; } else @@ -1700,7 +1698,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, buf, &short_entry_size); - if (status) + if (unlikely (status)) return status; } font->widths[i] = be16_to_cpu (*((int16_t*)buf)); @@ -1719,10 +1717,7 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_cff_font_t *font; tt_head_t head; tt_hhea_t hhea; - tt_name_t *name; - tt_name_record_t *record; unsigned long size, data_length; - int i, j; backend = scaled_font_subset->scaled_font->backend; if (!backend->load_truetype_table) @@ -1731,61 +1726,43 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, data_length = 0; status = backend->load_truetype_table( scaled_font_subset->scaled_font, TT_TAG_CFF, 0, NULL, &data_length); - if (status) + if (unlikely (status)) return status; size = sizeof (tt_head_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char *) &head, &size); - if (status) + if (unlikely (status)) return status; size = sizeof (tt_hhea_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); - if (status) + if (unlikely (status)) return status; size = 0; status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_hmtx, 0, NULL, &size); - if (status) + if (unlikely (status)) return status; - size = 0; - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_name, 0, NULL, &size); - if (status) - return status; - - name = malloc (size); - if (name == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_name, 0, - (unsigned char *) name, &size); - if (status) - goto fail1; - font = malloc (sizeof (cairo_cff_font_t)); - if (font == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail1; - } + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = backend; font->scaled_font_subset = scaled_font_subset; _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); - if (status) + if (unlikely (status)) goto fail2; font->subset_font_name = strdup (subset_name); - if (font->subset_font_name == NULL) { + if (unlikely (font->subset_font_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } @@ -1796,77 +1773,56 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, font->ascent = (int16_t) be16_to_cpu (hhea.ascender); font->descent = (int16_t) be16_to_cpu (hhea.descender); - /* Extract the font name from the name table. At present this - * just looks for the Mac platform/Roman encoded font name. It - * should be extended to use any suitable font name in the - * name table. If the mac/roman font name is not found a - * CairoFont-x-y name is created. - */ font->font_name = NULL; - for (i = 0; i < be16_to_cpu(name->num_records); i++) { - record = &(name->records[i]); - if ((be16_to_cpu (record->platform) == 1) && - (be16_to_cpu (record->encoding) == 0) && - (be16_to_cpu (record->name) == 4)) { - font->font_name = malloc (be16_to_cpu(record->length) + 1); - if (font->font_name) { - strncpy(font->font_name, - ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), - be16_to_cpu (record->length)); - font->font_name[be16_to_cpu (record->length)] = 0; - } - break; - } - } + status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + &font->ps_name, + &font->font_name); + if (_cairo_status_is_error (status)) + goto fail3; - if (font->font_name == NULL) { - font->font_name = malloc (30); - if (font->font_name == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->ps_name == NULL) { + font->ps_name = malloc (30); + if (unlikely (font->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; - } - snprintf(font->font_name, 30, "CairoFont-%u-%u", + } + + snprintf(font->ps_name, 30, "CairoFont-%u-%u", scaled_font_subset->font_id, scaled_font_subset->subset_id); } - for (i = 0, j = 0; font->font_name[j]; j++) { - if (font->font_name[j] == ' ') - continue; - font->font_name[i++] = font->font_name[j]; - } - font->font_name[i] = '\0'; - font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); - if (font->widths == NULL) { + if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } status = cairo_cff_font_create_set_widths (font); - if (status) + if (unlikely (status)) goto fail5; font->data_length = data_length; font->data = malloc (data_length); - if (font->data == NULL) { + if (unlikely (font->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail5; } status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font, TT_TAG_CFF, 0, font->data, &font->data_length); - if (status) + if (unlikely (status)) goto fail6; font->data_end = font->data + font->data_length; status = cff_dict_init (&font->top_dict); - if (status) + if (unlikely (status)) goto fail6; status = cff_dict_init (&font->private_dict); - if (status) + if (unlikely (status)) goto fail7; cff_index_init (&font->strings_index); @@ -1883,7 +1839,6 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, font->fd_subset_map = NULL; font->private_dict_offset = NULL; - free (name); *font_return = font; return CAIRO_STATUS_SUCCESS; @@ -1895,14 +1850,14 @@ fail6: fail5: free (font->widths); fail4: - free (font->font_name); + if (font->font_name) + free (font->font_name); fail3: free (font->subset_font_name); fail2: _cairo_array_fini (&font->output); free (font); -fail1: - free (name); + return status; } @@ -1912,7 +1867,9 @@ cairo_cff_font_destroy (cairo_cff_font_t *font) unsigned int i; free (font->widths); - free (font->font_name); + if (font->font_name) + free (font->font_name); + free (font->ps_name); free (font->subset_font_name); _cairo_array_fini (&font->output); cff_dict_fini (font->top_dict); @@ -1975,23 +1932,33 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, unsigned int i; status = _cairo_cff_font_create (font_subset, &font, subset_name); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_generate (font, &data, &length); - if (status) + if (unlikely (status)) goto fail1; - cff_subset->base_font = strdup (font->font_name); - if (cff_subset->base_font == NULL) { + cff_subset->ps_name = strdup (font->ps_name); + if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } + if (font->font_name) { + cff_subset->font_name = strdup (font->font_name); + if (cff_subset->font_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + } else { + cff_subset->font_name = NULL; + } + cff_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); - if (cff_subset->widths == NULL) { + if (unlikely (cff_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; + goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) cff_subset->widths[i] = font->widths[i]; @@ -2004,9 +1971,9 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, cff_subset->descent = font->descent; cff_subset->data = malloc (length); - if (cff_subset->data == NULL) { + if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; + goto fail4; } memcpy (cff_subset->data, data, length); @@ -2016,10 +1983,13 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, return CAIRO_STATUS_SUCCESS; - fail3: + fail4: free (cff_subset->widths); + fail3: + if (cff_subset->font_name) + free (cff_subset->font_name); fail2: - free (cff_subset->base_font); + free (cff_subset->ps_name); fail1: cairo_cff_font_destroy (font); @@ -2029,7 +1999,9 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, void _cairo_cff_subset_fini (cairo_cff_subset_t *subset) { - free (subset->base_font); + free (subset->ps_name); + if (subset->font_name) + free (subset->font_name); free (subset->widths); free (subset->data); } @@ -2043,7 +2015,7 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset cairo_cff_font_t *font; font = malloc (sizeof (cairo_cff_font_t)); - if (font == NULL) + if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = NULL; @@ -2051,20 +2023,21 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); - if (status) + if (unlikely (status)) goto fail1; font->subset_font_name = strdup (subset_name); - if (font->subset_font_name == NULL) { + if (unlikely (font->subset_font_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } - font->font_name = strdup (subset_name); - if (font->subset_font_name == NULL) { + font->ps_name = strdup (subset_name); + if (unlikely (font->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } + font->font_name = NULL; font->x_min = 0; font->y_min = 0; @@ -2074,7 +2047,7 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset font->descent = 0; font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); - if (font->widths == NULL) { + if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } @@ -2084,11 +2057,11 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset font->data_end = NULL; status = cff_dict_init (&font->top_dict); - if (status) + if (unlikely (status)) goto fail4; status = cff_dict_init (&font->private_dict); - if (status) + if (unlikely (status)) goto fail5; cff_index_init (&font->strings_index); @@ -2114,7 +2087,9 @@ fail5: fail4: free (font->widths); fail3: - free (font->font_name); + if (font->font_name) + free (font->font_name); + free (font->ps_name); fail2: free (font->subset_font_name); fail1: @@ -2151,37 +2126,37 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font, end_buf = encode_integer (end_buf, type2_subset->y_max); status = cff_dict_set_operands (font->top_dict, FONTBBOX_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, FDSELECT_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, FDARRAY_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); - if (status) + if (unlikely (status)) return status; status = cairo_cff_font_set_ros_strings (font); - if (status) + if (unlikely (status)) return status; /* Create CID FD dictionary */ status = cairo_cff_font_create_cid_fontdict (font); - if (status) + if (unlikely (status)) return status; /* Create charstrings */ @@ -2192,12 +2167,12 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font, _cairo_array_index (charstring, 0), _cairo_array_num_elements (charstring)); - if (status) + if (unlikely (status)) return status; } status = cairo_cff_font_write_subset (font); - if (status) + if (unlikely (status)) return status; *data = _cairo_array_index (&font->output, 0); @@ -2219,28 +2194,30 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, cairo_type2_charstrings_t type2_subset; status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name); - if (status) + if (unlikely (status)) return status; status = _cairo_type2_charstrings_init (&type2_subset, font_subset); - if (status) + if (unlikely (status)) goto fail1; status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length); - if (status) + if (unlikely (status)) goto fail2; - cff_subset->base_font = strdup (font->font_name); - if (cff_subset->base_font == NULL) { + cff_subset->font_name = NULL; + cff_subset->ps_name = strdup (font->ps_name); + if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } cff_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); - if (cff_subset->widths == NULL) { + if (unlikely (cff_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) cff_subset->widths[i] = type2_subset.widths[i]; @@ -2252,7 +2229,7 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, cff_subset->descent = type2_subset.y_min; cff_subset->data = malloc (length); - if (cff_subset->data == NULL) { + if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } @@ -2269,7 +2246,7 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, fail4: free (cff_subset->widths); fail3: - free (cff_subset->base_font); + free (cff_subset->ps_name); fail2: _cairo_type2_charstrings_fini (&type2_subset); fail1: @@ -2281,7 +2258,7 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, void _cairo_cff_fallback_fini (cairo_cff_subset_t *subset) { - free (subset->base_font); + free (subset->ps_name); free (subset->widths); free (subset->data); } diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h index 36c0fbd..4229e4f 100644 --- a/src/cairo-clip-private.h +++ b/src/cairo-clip-private.h @@ -40,7 +40,6 @@ #include "cairo-compiler-private.h" #include "cairo-path-fixed-private.h" #include "cairo-reference-count-private.h" -#include "cairo-region-private.h" extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; @@ -78,8 +77,7 @@ struct _cairo_clip { /* * A clip region that can be placed in the surface */ - cairo_region_t region; - cairo_bool_t has_region; + cairo_region_t *region; /* * If the surface supports path clipping, we store the list of * clipping paths that has been set here as a linked list. diff --git a/src/cairo-clip.c b/src/cairo-clip.c index b00ca39..bb04a9e 100644 --- a/src/cairo-clip.c +++ b/src/cairo-clip.c @@ -49,7 +49,7 @@ _cairo_clip_path_destroy (cairo_clip_path_t *clip_path); void _cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target) { - if (target) + if (target && target->backend) clip->mode = _cairo_surface_get_clip_mode (target); else clip->mode = CAIRO_CLIP_MODE_MASK; @@ -64,8 +64,7 @@ _cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target) clip->serial = 0; - _cairo_region_init (&clip->region); - clip->has_region = FALSE; + clip->region = NULL; clip->path = NULL; } @@ -76,28 +75,29 @@ _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) clip->mode = other->mode; clip->all_clipped = other->all_clipped; - + clip->surface = cairo_surface_reference (other->surface); clip->surface_rect = other->surface_rect; clip->serial = other->serial; - _cairo_region_init (&clip->region); - - if (other->has_region) { + if (other->region) { cairo_status_t status; + + clip->region = cairo_region_copy (other->region); - status = _cairo_region_copy (&clip->region, &other->region); - if (status) { - _cairo_region_fini (&clip->region); + status = cairo_region_status (clip->region); + if (unlikely (status)) { cairo_surface_destroy (clip->surface); + cairo_region_destroy (clip->region); + clip->region = NULL; + return status; } - clip->has_region = TRUE; } else { - clip->has_region = FALSE; + clip->region = NULL; } - + clip->path = _cairo_clip_path_reference (other->path); return CAIRO_STATUS_SUCCESS; @@ -114,14 +114,10 @@ _cairo_clip_reset (cairo_clip_t *clip) clip->serial = 0; - if (clip->has_region) { - /* _cairo_region_fini just releases the resources used but - * doesn't bother with leaving the region in a valid state. - * So _cairo_region_init has to be called afterwards. */ - _cairo_region_fini (&clip->region); - _cairo_region_init (&clip->region); + if (clip->region) { + cairo_region_destroy (clip->region); - clip->has_region = FALSE; + clip->region = NULL; } _cairo_clip_path_destroy (clip->path); @@ -143,30 +139,11 @@ _cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t *clip_path, cairo_rectangle_int_t *rectangle) { while (clip_path) { - cairo_status_t status; - cairo_traps_t traps; - cairo_box_t extents; - cairo_rectangle_int_t extents_rect; - - _cairo_box_from_rectangle (&extents, rectangle); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &extents); - - status = _cairo_path_fixed_fill_to_traps (&clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - &traps); - if (status) { - _cairo_traps_fini (&traps); - return status; - } + cairo_rectangle_int_t extents; - _cairo_traps_extents (&traps, &extents); - _cairo_traps_fini (&traps); + _cairo_path_fixed_approximate_clip_extents (&clip_path->path, &extents); - _cairo_box_round_to_rectangle (&extents, &extents_rect); - if (! _cairo_rectangle_intersect (rectangle, &extents_rect)) + if (! _cairo_rectangle_intersect (rectangle, &extents)) return CAIRO_STATUS_SUCCESS; clip_path = clip_path->prev; @@ -193,25 +170,17 @@ _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip, if (clip->path) { status = _cairo_clip_path_intersect_to_rectangle (clip->path, rectangle); - if (status) + if (unlikely (status)) return status; } - if (clip->has_region) { - cairo_region_t intersection; - - _cairo_region_init_rect (&intersection, rectangle); + if (clip->region) { + cairo_rectangle_int_t extents; - status = _cairo_region_intersect (&intersection, &clip->region, - &intersection); - - if (!status) - _cairo_region_get_extents (&intersection, rectangle); - - _cairo_region_fini (&intersection); - - if (status) - return status; + cairo_region_get_extents (clip->region, &extents); + is_empty = _cairo_rectangle_intersect (rectangle, &extents); + if (is_empty) + return CAIRO_STATUS_SUCCESS; } if (clip->surface) @@ -221,7 +190,7 @@ _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip, } cairo_status_t -_cairo_clip_intersect_to_region (cairo_clip_t *clip, +_cairo_clip_intersect_to_region (cairo_clip_t *clip, cairo_region_t *region) { cairo_status_t status; @@ -229,40 +198,21 @@ _cairo_clip_intersect_to_region (cairo_clip_t *clip, if (!clip) return CAIRO_STATUS_SUCCESS; - if (clip->all_clipped) { - cairo_region_t clip_rect; - - _cairo_region_init_rect (&clip_rect, &clip->surface_rect); - - status = _cairo_region_intersect (region, &clip_rect, region); - - _cairo_region_fini (&clip_rect); - - return status; - } + if (clip->all_clipped) + return cairo_region_intersect_rectangle (region, &clip->surface_rect); if (clip->path) { /* Intersect clip path into region. */ } - if (clip->has_region) { - status = _cairo_region_intersect (region, &clip->region, region); - if (status) + if (clip->region) { + status = cairo_region_intersect (region, clip->region); + if (unlikely (status)) return status; } - if (clip->surface) { - cairo_region_t clip_rect; - - _cairo_region_init_rect (&clip_rect, &clip->surface_rect); - - status = _cairo_region_intersect (region, &clip_rect, region); - - _cairo_region_fini (&clip_rect); - - if (status) - return status; - } + if (clip->surface) + return cairo_region_intersect_rectangle (region, &clip->surface_rect); return CAIRO_STATUS_SUCCESS; } @@ -317,11 +267,11 @@ _cairo_clip_intersect_path (cairo_clip_t *clip, return CAIRO_INT_STATUS_UNSUPPORTED; clip_path = malloc (sizeof (cairo_clip_path_t)); - if (clip_path == NULL) + if (unlikely (clip_path == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_path_fixed_init_copy (&clip_path->path, path); - if (status) { + if (unlikely (status)) { free (clip_path); return status; } @@ -371,7 +321,7 @@ _cairo_clip_intersect_region (cairo_clip_t *clip, cairo_traps_t *traps, cairo_surface_t *target) { - cairo_region_t region; + cairo_region_t *region; cairo_int_status_t status; if (clip->all_clipped) @@ -381,33 +331,24 @@ _cairo_clip_intersect_region (cairo_clip_t *clip, return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_traps_extract_region (traps, ®ion); - if (status) return status; - if (!clip->has_region) { - status = _cairo_region_copy (&clip->region, ®ion); - if (status == CAIRO_STATUS_SUCCESS) - clip->has_region = TRUE; + if (clip->region) { + status = cairo_region_intersect (clip->region, region); } else { - cairo_region_t intersection; - - _cairo_region_init (&intersection); - - status = _cairo_region_intersect (&intersection, - &clip->region, - ®ion); + clip->region = cairo_region_copy (region); - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_region_copy (&clip->region, &intersection); - - _cairo_region_fini (&intersection); + assert (clip->region != NULL); + + if ((status = cairo_region_status (clip->region))) + clip->region = NULL; } clip->serial = _cairo_surface_allocate_clip_serial (target); - _cairo_region_fini (®ion); + cairo_region_destroy (region); - if (! _cairo_region_not_empty (&clip->region)) + if (!clip->region || cairo_region_is_empty (clip->region)) _cairo_clip_set_all_clipped (clip, target); return status; @@ -478,9 +419,10 @@ _cairo_clip_intersect_mask (cairo_clip_t *clip, I believe the best possible operation would probably an unbounded SRC operator. Using SRC we could potentially avoid having to initialize the surface which would be ideal from an efficiency point of view. - However, _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) is - bounded by the mask. - + However, CAIRO_OPERATOR_SOURCE is bounded by the trapezoid mask and + _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) will assert + because it assumes CAIRO_OPERATOR_SOURCE has been converted into other + operations. */ surface = _cairo_surface_create_similar_solid (target, @@ -510,7 +452,7 @@ _cairo_clip_intersect_mask (cairo_clip_t *clip, _cairo_pattern_fini (&pattern.base); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (surface); return status; } @@ -535,7 +477,7 @@ _cairo_clip_intersect_mask (cairo_clip_t *clip, _cairo_pattern_fini (&pattern.base); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (surface); return status; } @@ -553,6 +495,134 @@ _cairo_clip_intersect_mask (cairo_clip_t *clip, return status; } +static cairo_status_t +_cairo_clip_intersect_mask_using_spans (cairo_clip_t *clip, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_surface_t *target) +{ + cairo_span_renderer_t *renderer = NULL; + cairo_pattern_union_t pattern; + cairo_rectangle_int_t surface_rect; + cairo_surface_t *surface = NULL; + cairo_status_t status; + cairo_operator_t op; + cairo_composite_rectangles_t rects; + + if (clip->all_clipped) + return CAIRO_STATUS_SUCCESS; + + _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + + /* If we have a clip surface we're going to use IN to combine our + * new clip with the old clip. The ADD is done to a transparent + * surface, as that's a fast way of doing it currently. We should + * really be using SOURCE instead, but _cairo_surface_composite() + * checks that it's not called with SOURCE or DEST. */ + op = clip->surface ? CAIRO_OPERATOR_IN : CAIRO_OPERATOR_ADD; + + /* Test if the target can composite spans. We're going to assume + * this is a good indicator of whether a similar surface is going + * to be able to composite spans too. */ + if ( !_cairo_surface_check_span_renderer (op, + &pattern.base, + target, + antialias, + NULL)) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto BAIL; + } + + status = _cairo_surface_get_extents (target, &surface_rect); + if (status) + goto BAIL; + + /* We'll create a new surface the size of the intersection of the + * old mask surface and the extents of the new clip path. */ + { + cairo_rectangle_int_t extents; + + _cairo_path_fixed_approximate_clip_extents (path, &extents); + if (! _cairo_rectangle_intersect (&surface_rect, &extents)) + goto SUCCESS; + + if (clip->surface != NULL && + ! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect)) + goto SUCCESS; + } + + /* Make the new mask surface and optionally initialise it from the + * previous clip if we have one. */ + surface = _cairo_surface_create_similar_solid (target, + CAIRO_CONTENT_ALPHA, + surface_rect.width, + surface_rect.height, + CAIRO_COLOR_TRANSPARENT); + if (surface->status) { + _cairo_pattern_fini (&pattern.base); + return surface->status; + } + + if (clip->surface) { + cairo_surface_pattern_t old_clip; + _cairo_pattern_init_for_surface (&old_clip, clip->surface); + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &old_clip.base, + NULL, + surface, + surface_rect.x - clip->surface_rect.x, + surface_rect.y - clip->surface_rect.y, + 0, 0, + 0, 0, + surface_rect.width, + surface_rect.height); + _cairo_pattern_fini (&old_clip.base); + if (status) + goto BAIL; + } + + _cairo_composite_rectangles_init (&rects, + surface_rect.x, + surface_rect.y, + surface_rect.width, + surface_rect.height); + rects.dst.x = 0; + rects.dst.y = 0; + + /* Render the new clipping path into the new mask surface. We've + * chosen op to either combine the new clip path with the existing + * clip mask (if there is one) or just render it. */ + status =_cairo_path_fixed_fill_using_spans (op, &pattern.base, + path, surface, + fill_rule, tolerance, + antialias, &rects); + if (status) + goto BAIL; + + SUCCESS: + if (clip->surface != NULL) + cairo_surface_destroy (clip->surface); + clip->surface = surface; + clip->surface_rect = surface_rect; + clip->serial = _cairo_surface_allocate_clip_serial (target); + surface = NULL; + + if (surface_rect.width == 0 || surface_rect.height == 0) + _cairo_clip_set_all_clipped (clip, target); + + BAIL: + if (renderer) + renderer->destroy(renderer); + if (surface) + cairo_surface_destroy (surface); + _cairo_pattern_fini (&pattern.base); + return status; +} + cairo_status_t _cairo_clip_clip (cairo_clip_t *clip, cairo_path_fixed_t *path, @@ -564,6 +634,7 @@ _cairo_clip_clip (cairo_clip_t *clip, cairo_status_t status; cairo_rectangle_int_t rectangle; cairo_traps_t traps; + cairo_box_t ignored_box; if (clip->all_clipped) return CAIRO_STATUS_SUCCESS; @@ -583,6 +654,18 @@ _cairo_clip_clip (cairo_clip_t *clip, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; + /* TODO: allow ANTIALIAS_NONE when we have a mono scan converter + * again. */ + if (antialias != CAIRO_ANTIALIAS_NONE && + !_cairo_path_fixed_is_box (path, &ignored_box) && + !_cairo_path_fixed_is_region (path)) + { + status = _cairo_clip_intersect_mask_using_spans ( + clip, path, fill_rule, tolerance, antialias, target); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + _cairo_traps_init (&traps); /* Limit the traps to the target surface @@ -599,7 +682,7 @@ _cairo_clip_clip (cairo_clip_t *clip, fill_rule, tolerance, &traps); - if (status) + if (unlikely (status)) goto bail; status = _cairo_clip_intersect_region (clip, &traps, target); @@ -622,10 +705,10 @@ _cairo_clip_translate (cairo_clip_t *clip, if (clip->all_clipped) return; - if (clip->has_region) { - _cairo_region_translate (&clip->region, - _cairo_fixed_integer_part (tx), - _cairo_fixed_integer_part (ty)); + if (clip->region) { + cairo_region_translate (clip->region, + _cairo_fixed_integer_part (tx), + _cairo_fixed_integer_part (ty)); } if (clip->surface) { @@ -656,7 +739,7 @@ _cairo_clip_path_reapply_clip_path (cairo_clip_t *clip, if (clip_path->prev) { status = _cairo_clip_path_reapply_clip_path (clip, clip_path->prev); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; } @@ -680,24 +763,23 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip, /* We should reapply the original clip path in this case, and let * whatever the right handling is happen */ } else { - if (other->has_region) { - status = _cairo_region_copy (&clip->region, &other->region); - if (status) + if (other->region) { + clip->region = cairo_region_copy (other->region); + if (unlikely ((status = cairo_region_status (clip->region)))) goto BAIL; - - clip->has_region = TRUE; } if (other->surface) { int dx, dy; status = _cairo_surface_clone_similar (target, other->surface, + CAIRO_CONTENT_ALPHA, 0, 0, other->surface_rect.width, other->surface_rect.height, &dx, &dy, &clip->surface); - if (status) + if (unlikely (status)) goto BAIL; clip->surface_rect = other->surface_rect; @@ -709,7 +791,7 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip, if (other->path) { status = _cairo_clip_path_reapply_clip_path (clip, other->path); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) goto BAIL; } } @@ -717,8 +799,8 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip, return CAIRO_STATUS_SUCCESS; BAIL: - if (clip->has_region) - _cairo_region_fini (&clip->region); + if (clip->region) + cairo_region_destroy (clip->region); if (clip->surface) cairo_surface_destroy (clip->surface); @@ -759,7 +841,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) { cairo_rectangle_list_t *list; cairo_rectangle_t *rectangles = NULL; - int n_boxes = 0; + int n_rects = 0; if (clip->all_clipped) goto DONE; @@ -769,46 +851,37 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; } - if (clip->has_region) { - cairo_box_int_t *boxes; + if (clip->region) { int i; - if (_cairo_region_get_boxes (&clip->region, &n_boxes, &boxes)) - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; + n_rects = cairo_region_num_rectangles (clip->region); - if (n_boxes) { - rectangles = _cairo_malloc_ab (n_boxes, sizeof (cairo_rectangle_t)); - if (rectangles == NULL) { - _cairo_region_boxes_fini (&clip->region, boxes); + if (n_rects) { + rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); + if (unlikely (rectangles == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; } - for (i = 0; i < n_boxes; ++i) { - cairo_rectangle_int_t clip_rect; - - clip_rect.x = boxes[i].p1.x; - clip_rect.y = boxes[i].p1.y; - clip_rect.width = boxes[i].p2.x - boxes[i].p1.x; - clip_rect.height = boxes[i].p2.y - boxes[i].p1.y; + for (i = 0; i < n_rects; ++i) { + cairo_rectangle_int_t clip_rect; + cairo_region_get_rectangle (clip->region, i, &clip_rect); + if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) { _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - _cairo_region_boxes_fini (&clip->region, boxes); free (rectangles); return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; } } } - - _cairo_region_boxes_fini (&clip->region, boxes); } else { cairo_rectangle_int_t extents; - n_boxes = 1; + n_rects = 1; rectangles = malloc(sizeof (cairo_rectangle_t)); - if (rectangles == NULL) { + if (unlikely (rectangles == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; } @@ -824,7 +897,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) DONE: list = malloc (sizeof (cairo_rectangle_list_t)); - if (list == NULL) { + if (unlikely (list == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); free (rectangles); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; @@ -832,7 +905,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) list->status = CAIRO_STATUS_SUCCESS; list->rectangles = rectangles; - list->num_rectangles = n_boxes; + list->num_rectangles = n_rects; return list; } diff --git a/src/cairo-combsort-private.h b/src/cairo-combsort-private.h new file mode 100644 index 0000000..d2fbb72 --- /dev/null +++ b/src/cairo-combsort-private.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2008 Chris Wilson + * + * 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 Chris Wilson + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +/* This fragment implements a comb sort (specifically combsort11) */ +#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP +#define _HAVE_CAIRO_COMBSORT_NEWGAP +static inline unsigned int +_cairo_combsort_newgap (unsigned int gap) +{ + gap = 10 * gap / 13; + if (gap == 9 || gap == 10) + gap = 11; + if (gap < 1) + gap = 1; + return gap; +} +#endif + +#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \ +static void \ +NAME (TYPE *base, unsigned int nmemb) \ +{ \ + unsigned int gap = nmemb; \ + unsigned int i, j; \ + int swapped; \ + do { \ + gap = _cairo_combsort_newgap (gap); \ + swapped = 0; \ + for (i = 0; i < nmemb-gap ; i++) { \ + j = i + gap; \ + if (CMP (base[i], base[j]) > 0 ) { \ + TYPE tmp; \ + tmp = base[i]; \ + base[i] = base[j]; \ + base[j] = tmp; \ + swapped = 1; \ + } \ + } \ + } while (gap > 1 || swapped); \ +} diff --git a/src/cairo-compiler-private.h b/src/cairo-compiler-private.h index b93fd82..403c3f7 100644 --- a/src/cairo-compiler-private.h +++ b/src/cairo-compiler-private.h @@ -139,6 +139,23 @@ #define cairo_const #endif +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define _CAIRO_BOOLEAN_EXPR(expr) \ + __extension__ ({ \ + int _cairo_boolean_var_; \ + if (expr) \ + _cairo_boolean_var_ = 1; \ + else \ + _cairo_boolean_var_ = 0; \ + _cairo_boolean_var_; \ +}) +#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1)) +#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + #ifndef __GNUC__ #undef __attribute__ #define __attribute__(x) @@ -147,6 +164,8 @@ #if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) #define snprintf _snprintf #define popen _popen +#define pclose _pclose +#define hypot _hypot #endif #ifdef _MSC_VER @@ -154,6 +173,30 @@ #define inline __inline #endif +#if defined(_MSC_VER) && defined(_M_IX86) +/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together. + The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and + will never be folded into another one. Something like this might eventually + be needed for GCC but it seems fine for now. */ +#define CAIRO_ENSURE_UNIQUE \ + do { \ + char func[] = __FUNCTION__; \ + char file[] = __FILE__; \ + __asm { \ + __asm jmp __internal_skip_line_no \ + __asm _emit (__LINE__ & 0xff) \ + __asm _emit ((__LINE__>>8) & 0xff) \ + __asm _emit ((__LINE__>>16) & 0xff) \ + __asm _emit ((__LINE__>>24) & 0xff) \ + __asm lea eax, func \ + __asm lea eax, file \ + __asm __internal_skip_line_no: \ + }; \ + } while (0) +#else +#define CAIRO_ENSURE_UNIQUE do { } while (0) +#endif + #ifdef __STRICT_ANSI__ #undef inline #define inline __inline__ diff --git a/src/cairo-debug.c b/src/cairo-debug.c index 61156f0..8d310a5 100644 --- a/src/cairo-debug.c +++ b/src/cairo-debug.c @@ -61,15 +61,59 @@ cairo_debug_reset_static_data (void) { CAIRO_MUTEX_INITIALIZE (); - _cairo_font_face_reset_static_data (); + _cairo_scaled_font_map_destroy (); + + _cairo_toy_font_face_reset_static_data (); #if CAIRO_HAS_FT_FONT _cairo_ft_font_reset_static_data (); #endif + _cairo_intern_string_reset_static_data (); + _cairo_scaled_font_reset_static_data (); _cairo_pattern_reset_static_data (); CAIRO_MUTEX_FINALIZE (); } + +#if HAVE_VALGRIND +void +_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface) +{ + const cairo_image_surface_t *image = (cairo_image_surface_t *) surface; + const uint8_t *bits; + int row, width; + + if (surface == NULL) + return; + + if (! RUNNING_ON_VALGRIND) + return; + + bits = image->data; + switch (image->format) { + case CAIRO_FORMAT_A1: + width = (image->width + 7)/8; + break; + case CAIRO_FORMAT_A8: + width = image->width; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + width = image->width*4; + break; + default: + /* XXX compute width from pixman bpp */ + return; + } + + for (row = 0; row < image->height; row++) { + VALGRIND_CHECK_MEM_IS_DEFINED (bits, width); + /* and then silence any future valgrind warnings */ + VALGRIND_MAKE_MEM_DEFINED (bits, width); + bits += image->stride; + } +} +#endif diff --git a/src/cairo-deflate-stream.c b/src/cairo-deflate-stream.c index bf2784a..863189f 100644 --- a/src/cairo-deflate-stream.c +++ b/src/cairo-deflate-stream.c @@ -121,13 +121,14 @@ _cairo_deflate_stream_create (cairo_output_stream_t *output) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (cairo_deflate_stream_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _cairo_deflate_stream_write, + NULL, _cairo_deflate_stream_close); stream->output = output; diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c index bb02dbf..1747117 100644 --- a/src/cairo-directfb-surface.c +++ b/src/cairo-directfb-surface.c @@ -455,7 +455,7 @@ _cairo_directfb_surface_create_similar (void *abstract_src, format = _cairo_format_from_content (content); surface = calloc (1, sizeof (cairo_directfb_surface_t)); - if (surface == NULL) + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface->dfb = source->dfb; @@ -617,6 +617,7 @@ _cairo_directfb_surface_release_dest_image (void *abstract_surf static cairo_status_t _cairo_directfb_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -703,8 +704,8 @@ _cairo_directfb_surface_clone_similar (void *abstract_surface, #if DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS static cairo_int_status_t _directfb_prepare_composite (cairo_directfb_surface_t *dst, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, cairo_operator_t op, int *src_x, int *src_y, int *mask_x, int *mask_y, @@ -733,7 +734,7 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, return CAIRO_INT_STATUS_UNSUPPORTED; if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) { - cairo_pattern_t *tmp; + const cairo_pattern_t *tmp; int tmp_x, tmp_y; if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID || @@ -762,13 +763,10 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, color = _cairo_stock_color (CAIRO_STOCK_WHITE); } - /* XXX DirectFB currently does not support filtering, so force NEAREST - * in order to hit optimisations inside core. - */ - src_pattern->filter = CAIRO_FILTER_NEAREST; - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, *src_x, *src_y, width, height, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT, (cairo_surface_t **) &src, &src_attr); if (status) @@ -843,7 +841,7 @@ _directfb_prepare_composite (cairo_directfb_surface_t *dst, static void _directfb_finish_composite (cairo_directfb_surface_t *dst, - cairo_pattern_t *src_pattern, + const cairo_pattern_t *src_pattern, cairo_surface_t *src, cairo_surface_attributes_t *src_attr) { @@ -893,8 +891,8 @@ _directfb_categorize_operation (cairo_surface_attributes_t *src_attr) static cairo_int_status_t _cairo_directfb_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, int mask_x, int mask_y, @@ -1161,7 +1159,7 @@ _cairo_directfb_surface_fill_rectangles (void *abstract_surface #if DFB_COMPOSITE_TRAPEZOIDS static cairo_int_status_t _cairo_directfb_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, int src_y, @@ -1304,41 +1302,40 @@ _cairo_directfb_surface_set_clip_region (void *abstract_surface, __FUNCTION__, surface, region); if (region) { - cairo_box_int_t *boxes; - int n_boxes; + int n_rects; cairo_status_t status; int i; surface->has_clip = TRUE; - status = _cairo_region_get_boxes (region, &n_boxes, &boxes); - if (n_boxes == 0) + n_rects = cairo_region_num_rectangles (region); + + if (n_rects == 0) return CAIRO_STATUS_SUCCESS; - if (status) - return status; - if (surface->n_clips != n_boxes) { + if (surface->n_clips != n_rects) { if (surface->clips) free (surface->clips); - surface->clips = _cairo_malloc_ab (n_boxes, sizeof (DFBRegion)); + surface->clips = _cairo_malloc_ab (n_rects, sizeof (DFBRegion)); if (!surface->clips) { surface->n_clips = 0; - _cairo_region_boxes_fini (region, boxes); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - surface->n_clips = n_boxes; + surface->n_clips = n_rects; } - for (i = 0; i < n_boxes; i++) { - surface->clips[i].x1 = boxes[i].p1.x; - surface->clips[i].y1 = boxes[i].p1.y; - surface->clips[i].x2 = boxes[i].p2.x - 1; - surface->clips[i].y2 = boxes[i].p2.y - 1; - } + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_boxes_fini (region, boxes); + cairo_region_get_rectangle (region, i, &rect); + + surface->clips[i].x1 = rect.x; + surface->clips[i].y1 = rect.y; + surface->clips[i].x2 = rect.x + rect.width - 1; + surface->clips[i].y2 = rect.y + rect.height - 1; + } } else { surface->has_clip = FALSE; if (surface->clips) { @@ -1433,7 +1430,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, D_DEBUG_AT (CairoDFB_Font, "%s( %p [%d] )\n", __FUNCTION__, glyphs, num_glyphs ); - _cairo_cache_freeze (scaled_font->glyphs); + _cairo_scaled_font_freeze_cache (scaled_font); if (scaled_font->surface_private) { cache = scaled_font->surface_private; @@ -1451,7 +1448,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (status) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return status; } @@ -1464,7 +1461,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, default: D_DEBUG_AT (CairoDFB_Font, " -> Unsupported font format %d!\n", img->format); - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -1502,7 +1499,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, /* Remember glyph location */ rect = malloc (sizeof (DFBRectangle)); if (rect == NULL) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } *rect = rects[n]; @@ -1522,7 +1519,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, } if (n == 0) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return CAIRO_INT_STATUS_NOTHING_TO_DO; } @@ -1532,7 +1529,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, /* XXX query maximum surface size */ if (w > 2048 || h > 2048) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -1549,7 +1546,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, w, h, &new_cache); if (status) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return status; } @@ -1564,7 +1561,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, status = _directfb_allocate_font_cache (surface->dfb, w, h, &cache); if (status) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return status; } @@ -1579,7 +1576,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, if (cache->dfbsurface->Lock (cache->dfbsurface, DSLF_WRITE, (void *)&data, &pitch)) { - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1645,7 +1642,7 @@ _directfb_acquire_font_cache (cairo_directfb_surface_t *surface, cache->dfbsurface->Unlock (cache->dfbsurface); } - _cairo_cache_thaw (scaled_font->glyphs); + _cairo_scaled_font_thaw_cache (scaled_font); cache->x = x; cache->y = y; @@ -1687,13 +1684,14 @@ _cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, } static cairo_int_status_t -_cairo_directfb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - cairo_pattern_t *pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) +_cairo_directfb_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_directfb_surface_t *dst = abstract_dst; cairo_directfb_font_cache_t *cache; @@ -1818,6 +1816,8 @@ _cairo_directfb_surface_backend = { #else NULL,/*composite_trapezoids*/ #endif + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_directfb_surface_set_clip_region,/* set_clip_region */ diff --git a/src/cairo-features.h b/src/cairo-features.h deleted file mode 100644 index 5bab718..0000000 --- a/src/cairo-features.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Generated by configure. Do not edit. */ -#ifndef CAIRO_FEATURES_H -#define CAIRO_FEATURES_H - -#define CAIRO_HAS_FT_FONT 1 -#define CAIRO_HAS_IMAGE_SURFACE 1 -#define CAIRO_HAS_PDF_SURFACE 1 -#define CAIRO_HAS_PNG_FUNCTIONS 1 -#define CAIRO_HAS_PS_SURFACE 1 -#define CAIRO_HAS_SVG_SURFACE 1 -#define CAIRO_HAS_USER_FONT 1 -#define CAIRO_HAS_XLIB_SURFACE 1 -#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 - -/*#undef CAIRO_HAS_QUARTZ_FONT */ -/*#undef CAIRO_HAS_QUARTZ_SURFACE */ -/*#undef CAIRO_HAS_WIN32_FONT */ -/*#undef CAIRO_HAS_WIN32_SURFACE */ - -#endif diff --git a/src/cairo-font-face-twin-data.c b/src/cairo-font-face-twin-data.c index 7a70324..ff09cb2 100644 --- a/src/cairo-font-face-twin-data.c +++ b/src/cairo-font-face-twin-data.c @@ -4,41 +4,47 @@ const int8_t _cairo_twin_outlines[] = { /* 0x0 '\0' offset 0 */ - 0, 24, 42, 0, 2, 4, + 0, 24, 42, 0, 2, 2, 0, 24, /* snap_x */ - -42, -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 24, -42, 'l', 24, 0, 'l', 0, 0, 'e', + 'X', 'X', /* 0x20 ' ' offset 28 */ - 0, 4, 0, 0, 2, 3, - -128, 0, /* snap_x */ - -21, -15, 0, /* snap_y */ + 0, 4, 0, 0, 0, 0, + /* snap_x */ + /* snap_y */ 'e', + 'X', 'X', 'X', + 'X', 'X', /* 0x21 '!' offset 40 */ - 0, 4, 42, 0, 3, 3, - 0, 2, 4, /* snap_x */ - -21, -15, 0, /* snap_y */ - 'm', 2, -42, - 'l', 2, -14, - 'm', 2, -4, - 'c', 1, -4, 0, -3, 0, -2, - 'c', 0, -1, 1, 0, 2, 0, - 'c', 3, 0, 4, -1, 4, -2, - 'c', 4, -3, 3, -4, 2, -4, + 0, 0, 42, 0, 1, 3, + 0, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -14, + 'm', 0, 0, + 'l', 0, 0, 'e', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', /* 0x22 '"' offset 90 */ - 0, 16, 42, -28, 2, 3, + 0, 16, 42, -28, 2, 2, 0, 16, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -28, /* snap_y */ 'm', 0, -42, 'l', 0, -28, 'm', 16, -42, 'l', 16, -28, 'e', + 'X', /* 0x23 '#' offset 114 */ 0, 30, 50, 14, 2, 5, 0, 30, /* snap_x */ @@ -102,27 +108,30 @@ const int8_t _cairo_twin_outlines[] = { 'c', 39, 0, 40, -1, 40, -4, 'e', /* 0x27 ''' offset 390 */ - 0, 4, 42, -30, 2, 3, + 0, 4, 42, -30, 2, 2, 0, 4, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -28, /* snap_y */ 'm', 2, -38, 'c', -1, -38, -1, -42, 2, -42, 'c', 6, -42, 5, -33, 0, -30, 'e', + 'X', /* 0x28 '(' offset 419 */ - 0, 14, 50, 14, 2, 3, + 0, 14, 50, 14, 2, 2, 0, 14, /* snap_x */ - -21, -15, 0, /* snap_y */ + -50, 14, /* snap_y */ 'm', 14, -50, 'c', -5, -32, -5, -5, 14, 14, 'e', + 'X', /* 0x29 ')' offset 441 */ - 0, 14, 50, 14, 2, 3, + 0, 14, 50, 14, 2, 2, 0, 14, /* snap_x */ - -21, -15, 0, /* snap_y */ + -15, 14, /* snap_y */ 'm', 0, -50, 'c', 19, -34, 19, -2, 0, 14, 'e', + 'X', /* 0x2a '*' offset 463 */ 0, 20, 30, -6, 3, 3, 0, 10, 20, /* snap_x */ @@ -187,7 +196,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x31 '1' offset 666 */ 0, 28, 42, 0, 2, 3, 0, 17, 28 /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -34, 0, /* snap_y */ 'm', 7, -34, 'c', 11, -35, 15, -38, 17, -42, 'l', 17, 0, @@ -215,15 +224,16 @@ const int8_t _cairo_twin_outlines[] = { 'c', 8, 0, 3, -1, 0, -8, 'e', /* 0x34 '4' offset 780 */ - 0, 28, 42, 0, 3, 4, + 0, 28, 42, 0, 3, 3, 0, 20, 30, /* snap_x */ - -21, -15, -14, 0, /* snap_y */ + -42, -14, 0, /* snap_y */ 'm', 20, 0, 'l', 20, -42, 'l', 0, -14, 'l', 30, -14, 'e', 'X', 'X', 'X', + 'X', /* 0x35 '5' offset 809 */ 0, 28, 42, 0, 2, 5, 0, 28, /* snap_x */ @@ -307,24 +317,25 @@ const int8_t _cairo_twin_outlines[] = { /* 0x3c '<' offset 1162 */ 0, 32, 36, 0, 2, 3, 0, 32, /* snap_x */ - -21, -15, 0, /* snap_y */ + -36, -18, 0, /* snap_y */ 'm', 32, -36, 'l', 0, -18, 'l', 32, 0, 'e', /* 0x3d '=' offset 1183 */ - 0, 36, 24, -12, 2, 5, + 0, 36, 24, -12, 2, 2, 0, 36, /* snap_x */ - -24, -21, -15, -12, 0, /* snap_y */ + -24, -15, /* snap_y */ 'm', 0, -24, 'l', 36, -24, 'm', 0, -12, 'l', 36, -12, 'e', + 'X', 'X', 'X', /* 0x3e '>' offset 1209 */ 0, 32, 36, 0, 2, 3, 0, 32, /* snap_x */ - -21, -15, 0, /* snap_y */ + -36, -18, 0, /* snap_y */ 'm', 0, -36, 'l', 32, -18, 'l', 0, 0, @@ -338,10 +349,13 @@ const int8_t _cairo_twin_outlines[] = { 'c', 24, -42, 24, -34, 24, -32, 'c', 24, -29, 24, -24, 12, -20, 'l', 12, -14, - 'm', 12, -4, - 'c', 9, -4, 9, 0, 12, 0, - 'c', 15, 0, 15, -4, 12, -4, + 'm', 12, 0, + 'l', 12, 0, 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', /* 0x40 '@' offset 1288 */ 0, 42, 42, 0, 1, 6, 30, /* snap_x */ @@ -359,9 +373,9 @@ const int8_t _cairo_twin_outlines[] = { 'c', 30, 0, 34, -3, 36, -6, 'e', /* 0x41 'A' offset 1375 */ - 0, 32, 42, 0, 2, 4, + 0, 32, 42, 0, 2, 3, 0, 32, /* snap_x */ - -21, -15, -14, 0, /* snap_y */ + -42, -14, 0, /* snap_y */ 'm', 0, 0, 'l', 16, -42, 'l', 32, 0, @@ -369,10 +383,11 @@ const int8_t _cairo_twin_outlines[] = { 'l', 26, -14, 'e', 'X', 'X', 'X', + 'X', /* 0x42 'B' offset 1406 */ - 0, 28, 42, 0, 2, 5, + 0, 28, 42, 0, 2, 3, 0, 28, /* snap_x */ - -42, -22, -21, -15, 0, /* snap_y */ + -42, -22, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 18, -42, @@ -383,6 +398,7 @@ const int8_t _cairo_twin_outlines[] = { 'E', 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', /* 0x43 'C' offset 1455 */ 0, 30, 42, 0, 2, 4, 0, 30, /* snap_x */ @@ -394,9 +410,9 @@ const int8_t _cairo_twin_outlines[] = { 'c', 21, 0, 26, 0, 30, -10, 'e', /* 0x44 'D' offset 1499 */ - 0, 28, 42, 0, 2, 4, + 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ - -42, -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 14, -42, @@ -404,10 +420,11 @@ const int8_t _cairo_twin_outlines[] = { 'E', 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', /* 0x45 'E' offset 1534 */ - 0, 26, 42, 0, 2, 5, + 0, 26, 42, 0, 2, 3, 0, 26, /* snap_x */ - -42, -22, -21, -15, 0, /* snap_y */ + -42, -22, 0, /* snap_y */ 'm', 26, -42, 'l', 0, -42, 'l', 0, 0, @@ -417,10 +434,11 @@ const int8_t _cairo_twin_outlines[] = { 'e', 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', /* 0x46 'F' offset 1572 */ - 0, 26, 42, 0, 2, 5, + 0, 26, 42, 0, 2, 3, 0, 26, /* snap_x */ - -42, -22, -21, -15, 0, /* snap_y */ + -42, -22, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 26, -42, @@ -428,6 +446,7 @@ const int8_t _cairo_twin_outlines[] = { 'l', 16, -22, 'e', 'X', 'X', 'X', + 'X', 'X', /* 0x47 'G' offset 1604 */ 0, 30, 42, 0, 2, 5, 0, 30, /* snap_x */ @@ -441,9 +460,9 @@ const int8_t _cairo_twin_outlines[] = { 'e', 'X', 'X', 'X', /* 0x48 'H' offset 1655 */ - 0, 28, 42, 0, 2, 4, + 0, 28, 42, 0, 2, 3, 0, 28, /* snap_x */ - -22, -21, -15, 0, /* snap_y */ + -42, -22, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 28, -42, @@ -451,17 +470,19 @@ const int8_t _cairo_twin_outlines[] = { 'm', 0, -22, 'l', 28, -22, 'e', + 'X', /* 0x49 'I' offset 1686 */ - 0, 0, 42, 0, 1, 3, + 0, 0, 42, 0, 1, 2, 0, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'e', + 'X', /* 0x4a 'J' offset 1703 */ 0, 20, 42, 0, 2, 3, 0, 20, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -15, 0, /* snap_y */ 'm', 20, -42, 'l', 20, -10, 'c', 20, 3, 0, 3, 0, -10, @@ -470,7 +491,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x4b 'K' offset 1731 */ 0, 28, 42, 0, 2, 3, 0, 28, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 28, -42, @@ -479,18 +500,19 @@ const int8_t _cairo_twin_outlines[] = { 'l', 28, 0, 'e', /* 0x4c 'L' offset 1761 */ - 0, 24, 42, 0, 2, 3, + 0, 24, 42, 0, 2, 2, 0, 24, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'l', 24, 0, 'e', 'X', 'X', 'X', + 'X', /* 0x4d 'M' offset 1785 */ - 0, 32, 42, 0, 2, 3, + 0, 32, 42, 0, 2, 2, 0, 32, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 16, 0, @@ -500,10 +522,11 @@ const int8_t _cairo_twin_outlines[] = { 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', + 'X', /* 0x4e 'N' offset 1821 */ - 0, 28, 42, 0, 2, 3, + 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 28, 0, @@ -511,6 +534,7 @@ const int8_t _cairo_twin_outlines[] = { 'e', 'X', 'X', 'X', 'X', 'X', 'X', + 'X', /* 0x4f 'O' offset 1851 */ 0, 32, 42, 0, 2, 4, 0, 32, /* snap_x */ @@ -541,7 +565,7 @@ const int8_t _cairo_twin_outlines[] = { 'c', 0, -13, 2, 0, 16, 0, 'c', 30, 0, 32, -13, 32, -21, 'c', 32, -29, 30, -42, 16, -42, - 'm', 18, -8, + 'M', 18, -8, 'l', 30, 4, 'e', /* 0x52 'R' offset 1981 */ @@ -578,27 +602,29 @@ const int8_t _cairo_twin_outlines[] = { 'l', 28, -42, 'e', /* 0x55 'U' offset 2100 */ - 0, 28, 42, 0, 2, 3, + 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, -12, 'c', 0, 4, 28, 4, 28, -12, 'l', 28, -42, 'e', + 'X', /* 0x56 'V' offset 2128 */ - 0, 32, 42, 0, 2, 3, + 0, 32, 42, 0, 2, 2, 0, 32, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 16, 0, 'l', 32, -42, 'e', 'X', 'X', 'X', + 'X', /* 0x57 'W' offset 2152 */ - 0, 40, 42, 0, 2, 3, + 0, 40, 42, 0, 2, 2, 0, 40, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 10, 0, 'l', 20, -42, @@ -608,19 +634,21 @@ const int8_t _cairo_twin_outlines[] = { 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', + 'X', /* 0x58 'X' offset 2188 */ - 0, 28, 42, 0, 2, 3, + 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 28, 0, 'm', 28, -42, 'l', 0, 0, 'e', + 'X', /* 0x59 'Y' offset 2212 */ 0, 32, 42, 0, 3, 3, 0, 16, 32, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -21, 0, /* snap_y */ 'm', 0, -42, 'l', 16, -22, 'l', 16, 0, @@ -673,21 +701,23 @@ const int8_t _cairo_twin_outlines[] = { 'e', 'X', 'X', 'X', /* 0x5f '_' offset 2363 */ - 0, 36, 0, 0, 2, 3, + 0, 36, 0, 0, 2, 1, 0, 36, /* snap_x */ - -21, -15, 0, /* snap_y */ + 0, /* snap_y */ 'm', 0, 0, 'l', 36, 0, 'e', + 'X', 'X', /* 0x60 '`' offset 2381 */ - 0, 4, 42, -30, 2, 3, + 0, 4, 42, -30, 2, 2, 0, 4, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 4, -42, 'c', 2, -40, 0, -39, 0, -32, 'c', 0, -31, 1, -30, 2, -30, 'c', 5, -30, 5, -34, 2, -34, 'e', + 'X', /* 0x61 'a' offset 2417 */ 0, 24, 28, 0, 2, 4, 0, 24, /* snap_x */ @@ -703,7 +733,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x62 'b' offset 2467 */ 0, 24, 42, 0, 2, 4, 0, 24, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -42, -28, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 0, -22, @@ -725,7 +755,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x64 'd' offset 2561 */ 0, 24, 42, 0, 2, 4, 0, 24, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -42, -28, -15, 0, /* snap_y */ 'm', 24, -42, 'l', 24, 0, 'm', 24, -22, @@ -772,7 +802,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x68 'h' offset 2758 */ 0, 22, 42, 0, 2, 4, 0, 22, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -42, -28, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 0, -20, @@ -780,30 +810,37 @@ const int8_t _cairo_twin_outlines[] = { 'l', 22, 0, 'e', /* 0x69 'i' offset 2790 */ - 0, 4, 44, 0, 3, 3, - 0, 2, 4, /* snap_x */ - -21, -15, 0, /* snap_y */ + 0, 0, 44, 0, 1, 3, + 0, /* snap_x */ + -42, -28, 0, /* snap_y */ 'm', 0, -42, - 'c', 0, -39, 4, -39, 4, -42, - 'c', 4, -45, 0, -45, 0, -42, - 'm', 2, -28, - 'l', 2, 0, + 'l', 0, -42, + 'm', 0, -28, + 'l', 0, 0, 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', + 'X', 'X', /* 0x6a 'j' offset 2826 */ - -8, 4, 44, 14, 3, 4, - 0, 2, 4, /* snap_x */ - -21, -15, 0, 14, /* snap_y */ - 'm', 0, -42, - 'c', 0, -39, 4, -39, 4, -42, - 'c', 4, -45, 0, -45, 0, -42, + -8, 4, 44, 14, 3, 5, + -8, 2, 4, /* snap_x */ + -42, -21, -15, 0, 14, /* snap_y */ + 'm', 2, -42, + 'l', 2, -42, 'm', 2, -28, 'l', 2, 6, 'c', 2, 13, -1, 14, -8, 14, 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', /* 0x6b 'k' offset 2870 */ 0, 22, 42, 0, 2, 3, 0, 22, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, -28, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 20, -28, @@ -812,16 +849,17 @@ const int8_t _cairo_twin_outlines[] = { 'l', 22, 0, 'e', /* 0x6c 'l' offset 2900 */ - 0, 0, 42, 0, 1, 3, + 0, 0, 42, 0, 1, 2, 0, /* snap_x */ - -21, -15, 0, /* snap_y */ + -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'e', + 'X', /* 0x6d 'm' offset 2917 */ - 0, 44, 28, 0, 3, 4, + 0, 44, 28, 0, 3, 3, 0, 22, 44, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -28, -21, 0, /* snap_y */ 'm', 0, -28, 'l', 0, 0, 'm', 0, -20, @@ -831,16 +869,18 @@ const int8_t _cairo_twin_outlines[] = { 'c', 27, -29, 44, -33, 44, -20, 'l', 44, 0, 'e', + 'X', /* 0x6e 'n' offset 2963 */ - 0, 22, 28, 0, 2, 4, + 0, 22, 28, 0, 2, 3, 0, 22, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -28, -21, 0, /* snap_y */ 'm', 0, -28, 'l', 0, 0, 'm', 0, -20, 'c', 4, -28, 22, -34, 22, -20, 'l', 22, 0, 'e', + 'X', /* 0x6f 'o' offset 2995 */ 0, 26, 28, 0, 2, 4, 0, 26, /* snap_x */ @@ -850,11 +890,11 @@ const int8_t _cairo_twin_outlines[] = { 'c', 0, -9, 2, 0, 13, 0, 'c', 24, 0, 26, -9, 26, -14, 'c', 26, -19, 24, -28, 13, -28, - 'e', + 'E', /* 0x70 'p' offset 3039 */ 0, 24, 28, 14, 2, 4, 0, 24, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -28, -21, 0, 14, /* snap_y */ 'm', 0, -28, 'l', 0, 14, 'm', 0, -22, @@ -866,7 +906,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x71 'q' offset 3089 */ 0, 24, 28, 14, 2, 4, 0, 24, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -28, -21, 0, 14, /* snap_y */ 'm', 24, -28, 'l', 24, 14, 'm', 24, -22, @@ -898,7 +938,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x74 't' offset 3219 */ 0, 16, 42, 0, 3, 4, 0, 6, 16, /* snap_x */ - -28, -21, -15, 0, /* snap_y */ + -42, -28, -21, 0, /* snap_y */ 'm', 6, -42, 'l', 6, -8, 'c', 6, -2, 8, 0, 16, 0, @@ -908,7 +948,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x75 'u' offset 3252 */ 0, 22, 28, 0, 2, 3, 0, 22, /* snap_x */ - -21, -15, 0, /* snap_y */ + -28, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 0, -8, 'c', 0, 6, 18, 0, 22, -8, @@ -918,7 +958,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x76 'v' offset 3283 */ 0, 24, 28, 0, 2, 3, 0, 24, /* snap_x */ - -21, -15, 0, /* snap_y */ + -28, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 12, 0, 'l', 24, -28, @@ -927,7 +967,7 @@ const int8_t _cairo_twin_outlines[] = { /* 0x77 'w' offset 3307 */ 0, 32, 28, 0, 2, 3, 0, 32, /* snap_x */ - -21, -15, 0, /* snap_y */ + -28, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 8, 0, 'l', 16, -28, @@ -938,18 +978,19 @@ const int8_t _cairo_twin_outlines[] = { 'X', 'X', 'X', 'X', 'X', 'X', /* 0x78 'x' offset 3343 */ - 0, 22, 28, 0, 2, 3, + 0, 22, 28, 0, 2, 2, 0, 22, /* snap_x */ - -21, -15, 0, /* snap_y */ + -28, 0, /* snap_y */ 'm', 0, -28, 'l', 22, 0, 'm', 22, -28, 'l', 0, 0, 'e', + 'X', /* 0x79 'y' offset 3367 */ -2, 24, 28, 14, 2, 4, 0, 24, /* snap_x */ - -21, -15, 0, 14, /* snap_y */ + -28, -15, 0, 14, /* snap_y */ 'm', 0, -28, 'l', 12, 0, 'm', 24, -28, @@ -980,12 +1021,13 @@ const int8_t _cairo_twin_outlines[] = { 'c', 6, -2, 10, 0, 16, 0, 'e', /* 0x7c '|' offset 3474 */ - 0, 0, 50, 14, 1, 3, + 0, 0, 50, 14, 1, 2, 0, /* snap_x */ - -21, -15, 0, /* snap_y */ + -50, 14, /* snap_y */ 'm', 0, -50, 'l', 0, 14, 'e', + 'X', /* 0x7d '}' offset 3491 */ 0, 16, 44, 0, 3, 5, 0, 10, 16, /* snap_x */ diff --git a/src/cairo-font-face-twin.c b/src/cairo-font-face-twin.c index d8e662f..712ca0d 100644 --- a/src/cairo-font-face-twin.c +++ b/src/cairo-font-face-twin.c @@ -36,15 +36,438 @@ #include "cairoint.h" +#include <math.h> + /* - * This file implements a user-font rendering the decendant of the Hershey + * This file implements a user-font rendering the descendant of the Hershey * font coded by Keith Packard for use in the Twin window system. * The actual font data is in cairo-font-face-twin-data.c * - * Ported to cairo user font by Behdad Esfahbod. + * Ported to cairo user font and extended by Behdad Esfahbod. */ + +static cairo_user_data_key_t twin_properties_key; + + +/* + * Face properties + */ + +/* We synthesize multiple faces from the twin data. Here is the parameters. */ + +/* The following tables and matching code are copied from Pango */ + +/* CSS weight */ +typedef enum { + TWIN_WEIGHT_THIN = 100, + TWIN_WEIGHT_ULTRALIGHT = 200, + TWIN_WEIGHT_LIGHT = 300, + TWIN_WEIGHT_BOOK = 380, + TWIN_WEIGHT_NORMAL = 400, + TWIN_WEIGHT_MEDIUM = 500, + TWIN_WEIGHT_SEMIBOLD = 600, + TWIN_WEIGHT_BOLD = 700, + TWIN_WEIGHT_ULTRABOLD = 800, + TWIN_WEIGHT_HEAVY = 900, + TWIN_WEIGHT_ULTRAHEAVY = 1000 +} twin_face_weight_t; + +/* CSS stretch */ +typedef enum { + TWIN_STRETCH_ULTRA_CONDENSED, + TWIN_STRETCH_EXTRA_CONDENSED, + TWIN_STRETCH_CONDENSED, + TWIN_STRETCH_SEMI_CONDENSED, + TWIN_STRETCH_NORMAL, + TWIN_STRETCH_SEMI_EXPANDED, + TWIN_STRETCH_EXPANDED, + TWIN_STRETCH_EXTRA_EXPANDED, + TWIN_STRETCH_ULTRA_EXPANDED +} twin_face_stretch_t; + +typedef struct +{ + int value; + const char str[16]; +} FieldMap; + +static const FieldMap slant_map[] = { + { CAIRO_FONT_SLANT_NORMAL, "" }, + { CAIRO_FONT_SLANT_NORMAL, "Roman" }, + { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" }, + { CAIRO_FONT_SLANT_ITALIC, "Italic" } +}; + +static const FieldMap smallcaps_map[] = { + { FALSE, "" }, + { TRUE, "Small-Caps" } +}; + +static const FieldMap weight_map[] = { + { TWIN_WEIGHT_THIN, "Thin" }, + { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" }, + { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" }, + { TWIN_WEIGHT_LIGHT, "Light" }, + { TWIN_WEIGHT_BOOK, "Book" }, + { TWIN_WEIGHT_NORMAL, "" }, + { TWIN_WEIGHT_NORMAL, "Regular" }, + { TWIN_WEIGHT_MEDIUM, "Medium" }, + { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" }, + { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" }, + { TWIN_WEIGHT_BOLD, "Bold" }, + { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" }, + { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" }, + { TWIN_WEIGHT_HEAVY, "Heavy" }, + { TWIN_WEIGHT_HEAVY, "Black" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" } +}; + +static const FieldMap stretch_map[] = { + { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, + { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, + { TWIN_STRETCH_CONDENSED, "Condensed" }, + { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, + { TWIN_STRETCH_NORMAL, "" }, + { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, + { TWIN_STRETCH_EXPANDED, "Expanded" }, + { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, + { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } +}; + +static const FieldMap monospace_map[] = { + { FALSE, "" }, + { TRUE, "Mono" }, + { TRUE, "Monospace" } +}; + + +typedef struct _twin_face_properties { + cairo_font_slant_t slant; + twin_face_weight_t weight; + twin_face_stretch_t stretch; + + /* lets have some fun */ + cairo_bool_t monospace; + cairo_bool_t smallcaps; +} twin_face_properties_t; + +static cairo_bool_t +field_matches (const char *s1, + const char *s2, + int len) +{ + int c1, c2; + + while (len && *s1 && *s2) + { +#define TOLOWER(c) \ + (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) + + c1 = TOLOWER (*s1); + c2 = TOLOWER (*s2); + if (c1 != c2) { + if (c1 == '-') { + s1++; + continue; + } + return FALSE; + } + s1++; s2++; + len--; + } + + return len == 0 && *s1 == '\0'; +} + +static cairo_bool_t +parse_int (const char *word, + size_t wordlen, + int *out) +{ + char *end; + long val = strtol (word, &end, 10); + int i = val; + + if (end != word && (end == word + wordlen) && val >= 0 && val == i) + { + if (out) + *out = i; + + return TRUE; + } + + return FALSE; +} + +static cairo_bool_t +find_field (const char *what, + const FieldMap *map, + int n_elements, + const char *str, + int len, + int *val) +{ + int i; + cairo_bool_t had_prefix = FALSE; + + if (what) + { + i = strlen (what); + if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') + { + str += i + 1; + len -= i + 1; + had_prefix = TRUE; + } + } + + for (i=0; i<n_elements; i++) + { + if (map[i].str[0] && field_matches (map[i].str, str, len)) + { + if (val) + *val = map[i].value; + return TRUE; + } + } + + if (!what || had_prefix) + return parse_int (str, len, val); + + return FALSE; +} + +static void +parse_field (twin_face_properties_t *props, + const char *str, + int len) +{ + if (field_matches ("Normal", str, len)) + return; + +#define FIELD(NAME) \ + if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \ + (int *)(void *)&props->NAME)) \ + return; \ + + FIELD (weight); + FIELD (slant); + FIELD (stretch); + FIELD (smallcaps); + FIELD (monospace); + +#undef FIELD +} + +static void +face_props_parse (twin_face_properties_t *props, + const char *s) +{ + const char *start, *end; + + for (start = end = s; *end; end++) { + if (*end != ' ' && *end != ':') + continue; + + if (start < end) + parse_field (props, start, end - start); + start = end + 1; + } + if (start < end) + parse_field (props, start, end - start); +} + +static cairo_status_t +twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, + cairo_toy_font_face_t *toy_face) +{ + cairo_status_t status; + twin_face_properties_t *props; + + props = malloc (sizeof (twin_face_properties_t)); + if (unlikely (props == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + props->stretch = TWIN_STRETCH_NORMAL; + props->monospace = FALSE; + props->smallcaps = FALSE; + + props->slant = toy_face->slant; + props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ? + TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD; + face_props_parse (props, toy_face->family); + + status = cairo_font_face_set_user_data (twin_face, + &twin_properties_key, + props, free); + if (unlikely (status)) + goto FREE_PROPS; + + return CAIRO_STATUS_SUCCESS; + +FREE_PROPS: + free (props); + return status; +} + + +/* + * Scaled properties + */ + +typedef struct _twin_scaled_properties { + twin_face_properties_t *face_props; + + cairo_bool_t snap; /* hint outlines */ + + double weight; /* unhinted pen width */ + double penx, peny; /* hinted pen width */ + double marginl, marginr; /* hinted side margins */ + + double stretch; /* stretch factor */ +} twin_scaled_properties_t; + +static void +compute_hinting_scale (cairo_t *cr, + double x, double y, + double *scale, double *inv) +{ + cairo_user_to_device_distance (cr, &x, &y); + *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y); + *inv = 1 / *scale; +} + +static void +compute_hinting_scales (cairo_t *cr, + double *x_scale, double *x_scale_inv, + double *y_scale, double *y_scale_inv) +{ + double x, y; + + x = 1; y = 0; + compute_hinting_scale (cr, x, y, x_scale, x_scale_inv); + + x = 0; y = 1; + compute_hinting_scale (cr, x, y, y_scale, y_scale_inv); +} + +#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv) +#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv) + +/* This controls the global font size */ +#define F(g) ((g) / 72.) + +static void +twin_hint_pen_and_margins(cairo_t *cr, + double *penx, double *peny, + double *marginl, double *marginr) +{ + double x_scale, x_scale_inv; + double y_scale, y_scale_inv; + double margin; + + compute_hinting_scales (cr, + &x_scale, &x_scale_inv, + &y_scale, &y_scale_inv); + + *penx = SNAPXI (*penx); + if (*penx < x_scale_inv) + *penx = x_scale_inv; + + *peny = SNAPYI (*peny); + if (*peny < y_scale_inv) + *peny = y_scale_inv; + + margin = *marginl + *marginr; + *marginl = SNAPXI (*marginl); + if (*marginl < x_scale_inv) + *marginl = x_scale_inv; + + *marginr = margin - *marginl; + if (*marginr < 0) + *marginr = 0; + *marginr = SNAPXI (*marginr); +} + +static cairo_status_t +twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, + cairo_t *cr) +{ + cairo_status_t status; + twin_scaled_properties_t *props; + + props = malloc (sizeof (twin_scaled_properties_t)); + if (unlikely (props == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + + props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &twin_properties_key); + + props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE; + + /* weight */ + props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL); + + /* pen & margins */ + props->penx = props->peny = props->weight; + props->marginl = props->marginr = F (4); + if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT) + twin_hint_pen_and_margins(cr, + &props->penx, &props->peny, + &props->marginl, &props->marginr); + + /* stretch */ + props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL); + + + /* Save it */ + status = cairo_scaled_font_set_user_data (scaled_font, + &twin_properties_key, + props, free); + if (unlikely (status)) + goto FREE_PROPS; + + return CAIRO_STATUS_SUCCESS; + +FREE_PROPS: + free (props); + return status; +} + + +/* + * User-font implementation + */ + +static cairo_status_t +twin_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_t *cr, + cairo_font_extents_t *metrics) +{ + metrics->ascent = F (54); + metrics->descent = 1 - metrics->ascent; + + return twin_scaled_font_compute_properties (scaled_font, cr); +} + +#define TWIN_GLYPH_MAX_SNAP_X 4 +#define TWIN_GLYPH_MAX_SNAP_Y 7 + +typedef struct { + int n_snap_x; + int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X]; + double snapped_x[TWIN_GLYPH_MAX_SNAP_X]; + int n_snap_y; + int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y]; + double snapped_y[TWIN_GLYPH_MAX_SNAP_Y]; +} twin_snap_info_t; + #define twin_glyph_left(g) ((g)[0]) #define twin_glyph_right(g) ((g)[1]) #define twin_glyph_ascent(g) ((g)[2]) @@ -56,50 +479,72 @@ #define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) #define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g)) -#define SNAPI(p) (p) -#define SNAPH(p) (p) +static void +twin_compute_snap (cairo_t *cr, + twin_snap_info_t *info, + const signed char *b) +{ + int s, n; + const signed char *snap; + double x_scale, x_scale_inv; + double y_scale, y_scale_inv; -#define FX(g) ((g) / 64.) -#define FY(g) ((g) / 64.) + compute_hinting_scales (cr, + &x_scale, &x_scale_inv, + &y_scale, &y_scale_inv); + snap = twin_glyph_snap_x (b); + n = twin_glyph_n_snap_x (b); + info->n_snap_x = n; + assert (n <= TWIN_GLYPH_MAX_SNAP_X); + for (s = 0; s < n; s++) { + info->snap_x[s] = snap[s]; + info->snapped_x[s] = SNAPXI (F (snap[s])); + } -static cairo_status_t -twin_scaled_font_init (cairo_scaled_font_t *scaled_font, - cairo_t *cr, - cairo_font_extents_t *metrics) -{ - metrics->ascent = FY (50); - metrics->descent = FY (14); - return CAIRO_STATUS_SUCCESS; + snap = twin_glyph_snap_y (b); + n = twin_glyph_n_snap_y (b); + info->n_snap_y = n; + assert (n <= TWIN_GLYPH_MAX_SNAP_Y); + for (s = 0; s < n; s++) { + info->snap_y[s] = snap[s]; + info->snapped_y[s] = SNAPYI (F (snap[s])); + } } -static cairo_status_t -twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, - unsigned long unicode, - unsigned long *glyph) +static double +twin_snap (int8_t v, int n, int8_t *snap, double *snapped) { - /* We use an identity charmap. Which means we could live - * with no unicode_to_glyph method too. But we define this - * to map all unknown chars to a single unknown glyph to - * reduce pressure on cache. */ + int s; - if (unicode < ARRAY_LENGTH (_cairo_twin_charmap)) - *glyph = unicode; - else - *glyph = 0; + if (!n) + return F(v); - return CAIRO_STATUS_SUCCESS; -} + if (snap[0] == v) + return snapped[0]; -#define SNAPX(p) _twin_snap (p, info.snap_x, info.n_snap_x) -#define SNAPY(p) _twin_snap (p, info.snap_y, info.n_snap_y) + for (s = 0; s < n - 1; s++) + { + if (snap[s+1] == v) + return snapped[s+1]; -static double -_twin_snap (double v, int a, int b) -{ - return v; /* XXX */ + if (snap[s] <= v && v <= snap[s+1]) + { + int before = snap[s]; + int after = snap[s+1]; + int dist = after - before; + double snap_before = snapped[s]; + double snap_after = snapped[s+1]; + double dist_before = v - before; + return snap_before + (snap_after - snap_before) * dist_before / dist; + } + } + return F(v); } +#define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x) +#define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y) + static cairo_status_t twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, unsigned long glyph, @@ -107,75 +552,113 @@ twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, cairo_text_extents_t *metrics) { double x1, y1, x2, y2, x3, y3; - const int8_t *b = _cairo_twin_outlines + - _cairo_twin_charmap[glyph >= ARRAY_LENGTH (_cairo_twin_charmap) ? 0 : glyph]; - const int8_t *g = twin_glyph_draw(b); - - struct { - cairo_bool_t snap; - int snap_x; - int snap_y; - int n_snap_x; - int n_snap_y; - } info = {FALSE}; - - cairo_set_line_width (cr, 0.06); - cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + double marginl; + twin_scaled_properties_t *props; + twin_snap_info_t info; + const int8_t *b; + const int8_t *g; + int8_t w; + double gw; + + props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key); + + /* Save glyph space, we need it when stroking */ + cairo_save (cr); + + /* center the pen */ + cairo_translate (cr, props->penx * .5, -props->peny * .5); + + /* small-caps */ + if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') { + glyph += 'A' - 'a'; + /* 28 and 42 are small and capital letter heights of the glyph data */ + cairo_scale (cr, 1, 28. / 42); + } + + /* slant */ + if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) { + cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0}; + cairo_transform (cr, &shear); + } + + b = _cairo_twin_outlines + + _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph]; + g = twin_glyph_draw(b); + w = twin_glyph_right(b); + gw = F(w); + + marginl = props->marginl; + + /* monospace */ + if (props->face_props->monospace) { + double monow = F(24); + double extra = props->penx + props->marginl + props->marginr; + cairo_scale (cr, (monow + extra) / (gw + extra), 1); + gw = monow; + + /* resnap margin for new transform */ + { + double x, y, x_scale, x_scale_inv; + x = 1; y = 0; + compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv); + marginl = SNAPXI (marginl); + } + } + + cairo_translate (cr, marginl, 0); + + /* stretch */ + cairo_scale (cr, props->stretch, 1); + + if (props->snap) + twin_compute_snap (cr, &info, b); + else + info.n_snap_x = info.n_snap_y = 0; + + /* advance width */ + metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr; + /* glyph shape */ for (;;) { switch (*g++) { case 'M': cairo_close_path (cr); /* fall through */ case 'm': - x1 = FX(*g++); - y1 = FY(*g++); - if (info.snap) - { - x1 = SNAPX (x1); - y1 = SNAPY (y1); - } + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); cairo_move_to (cr, x1, y1); continue; case 'L': cairo_close_path (cr); /* fall through */ case 'l': - x1 = FX(*g++); - y1 = FY(*g++); - if (info.snap) - { - x1 = SNAPX (x1); - y1 = SNAPY (y1); - } + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); cairo_line_to (cr, x1, y1); continue; case 'C': cairo_close_path (cr); /* fall through */ case 'c': - x1 = FX(*g++); - y1 = FY(*g++); - x2 = FX(*g++); - y2 = FY(*g++); - x3 = FX(*g++); - y3 = FY(*g++); - if (info.snap) - { - x1 = SNAPX (x1); - y1 = SNAPY (y1); - x2 = SNAPX (x2); - y2 = SNAPY (y2); - x3 = SNAPX (x3); - y3 = SNAPY (y3); - } + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + x2 = SNAPX(*g++); + y2 = SNAPY(*g++); + x3 = SNAPX(*g++); + y3 = SNAPY(*g++); cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); continue; case 'E': cairo_close_path (cr); /* fall through */ case 'e': + cairo_restore (cr); /* restore glyph space */ + cairo_set_tolerance (cr, 0.01); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr, 1); + cairo_scale (cr, props->penx, props->peny); cairo_stroke (cr); break; case 'X': @@ -185,25 +668,50 @@ twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, break; } - metrics->x_advance = FX(twin_glyph_right(b)) + cairo_get_line_width (cr); - metrics->x_advance += cairo_get_line_width (cr)/* XXX 2*x.margin */; - if (info.snap) - metrics->x_advance = SNAPI (SNAPX (metrics->x_advance)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph) +{ + /* We use an identity charmap. Which means we could live + * with no unicode_to_glyph method too. But we define this + * to map all unknown chars to a single unknown glyph to + * reduce pressure on cache. */ + if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap))) + *glyph = unicode; + else + *glyph = 0; return CAIRO_STATUS_SUCCESS; } -cairo_font_face_t * -_cairo_font_face_twin_create (cairo_font_slant_t slant, - cairo_font_weight_t weight) + +/* + * Face constructor + */ + +cairo_status_t +_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) { + cairo_status_t status; cairo_font_face_t *twin_font_face; twin_font_face = cairo_user_font_face_create (); cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init); cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph); cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph); + status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face); + if (status) { + cairo_font_face_destroy (twin_font_face); + return status; + } + + *font_face = twin_font_face; - return twin_font_face; + return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo-font-face.c b/src/cairo-font-face.c index 30c8d9f..6744899 100644 --- a/src/cairo-font-face.c +++ b/src/cairo-font-face.c @@ -38,81 +38,16 @@ * Owen Taylor <otaylor@redhat.com> */ -#define _BSD_SOURCE /* for strdup() */ #include "cairoint.h" -static const cairo_font_face_backend_t _cairo_toy_font_face_backend; - /* #cairo_font_face_t */ -const cairo_toy_font_face_t _cairo_font_face_nil = { - { +const cairo_font_face_t _cairo_font_face_nil = { { 0 }, /* hash_entry */ CAIRO_STATUS_NO_MEMORY, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ - &_cairo_toy_font_face_backend - }, - CAIRO_FONT_FAMILY_DEFAULT, /* family */ - TRUE, /* owns_family */ - CAIRO_FONT_SLANT_DEFAULT, /* slant */ - CAIRO_FONT_WEIGHT_DEFAULT /* weight */ -}; - -static const cairo_toy_font_face_t _cairo_font_face_null_pointer = { - { - { 0 }, /* hash_entry */ - CAIRO_STATUS_NULL_POINTER, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - &_cairo_toy_font_face_backend - }, - CAIRO_FONT_FAMILY_DEFAULT, /* family */ - TRUE, /* owns_family */ - CAIRO_FONT_SLANT_DEFAULT, /* slant */ - CAIRO_FONT_WEIGHT_DEFAULT /* weight */ -}; - -static const cairo_toy_font_face_t _cairo_font_face_invalid_string = { - { - { 0 }, /* hash_entry */ - CAIRO_STATUS_INVALID_STRING, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - &_cairo_toy_font_face_backend - }, - CAIRO_FONT_FAMILY_DEFAULT, /* family */ - TRUE, /* owns_family */ - CAIRO_FONT_SLANT_DEFAULT, /* slant */ - CAIRO_FONT_WEIGHT_DEFAULT /* weight */ -}; - -static const cairo_toy_font_face_t _cairo_font_face_invalid_slant = { - { - { 0 }, /* hash_entry */ - CAIRO_STATUS_INVALID_SLANT, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - &_cairo_toy_font_face_backend - }, - CAIRO_FONT_FAMILY_DEFAULT, /* family */ - TRUE, /* owns_family */ - CAIRO_FONT_SLANT_DEFAULT, /* slant */ - CAIRO_FONT_WEIGHT_DEFAULT /* weight */ -}; - -static const cairo_toy_font_face_t _cairo_font_face_invalid_weight = { - { - { 0 }, /* hash_entry */ - CAIRO_STATUS_INVALID_WEIGHT, /* status */ - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - { 0, 0, 0, NULL }, /* user_data */ - &_cairo_toy_font_face_backend - }, - CAIRO_FONT_FAMILY_DEFAULT, /* family */ - TRUE, /* owns_family */ - CAIRO_FONT_SLANT_DEFAULT, /* slant */ - CAIRO_FONT_WEIGHT_DEFAULT /* weight */ + NULL }; cairo_status_t @@ -223,6 +158,9 @@ slim_hidden_def (cairo_font_face_destroy); cairo_font_type_t cairo_font_face_get_type (cairo_font_face_t *font_face) { + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return CAIRO_FONT_TYPE_TOY; + return font_face->backend->type; } @@ -314,416 +252,6 @@ cairo_font_face_set_user_data (cairo_font_face_t *font_face, } slim_hidden_def (cairo_font_face_set_user_data); -static const cairo_font_face_backend_t _cairo_toy_font_face_backend; - -static int -_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 - * this mapping is to provide unique #cairo_font_face_t values so that - * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t - * works. Once the corresponding #cairo_font_face_t objects fall out of - * downstream caches, we don't need them in this hash table anymore. - * - * Modifications to this hash table are protected by - * _cairo_font_face_mutex. - */ -static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL; - -static cairo_hash_table_t * -_cairo_toy_font_face_hash_table_lock (void) -{ - CAIRO_MUTEX_LOCK (_cairo_font_face_mutex); - - if (cairo_toy_font_face_hash_table == NULL) - { - cairo_toy_font_face_hash_table = - _cairo_hash_table_create (_cairo_toy_font_face_keys_equal); - - if (cairo_toy_font_face_hash_table == NULL) { - CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex); - return NULL; - } - } - - return cairo_toy_font_face_hash_table; -} - -static void -_cairo_toy_font_face_hash_table_unlock (void) -{ - CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex); -} - -/** - * _cairo_toy_font_face_init_key: - * - * Initialize those portions of #cairo_toy_font_face_t needed to use - * it as a hash table key, including the hash code buried away in - * font_face->base.hash_entry. No memory allocation is performed here - * so that no fini call is needed. We do this to make it easier to use - * an automatic #cairo_toy_font_face_t variable as a key. - **/ -static void -_cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - unsigned long hash; - - key->family = family; - key->owns_family = FALSE; - - key->slant = slant; - key->weight = weight; - - /* 1607 and 1451 are just a couple of arbitrary primes. */ - hash = _cairo_hash_string (family); - hash += ((unsigned long) slant) * 1607; - hash += ((unsigned long) weight) * 1451; - - assert (hash != 0); - key->base.hash_entry.hash = hash; -} - -static cairo_status_t -_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - char *family_copy; - - family_copy = strdup (family); - if (family_copy == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_toy_font_face_init_key (font_face, family_copy, - slant, weight); - font_face->owns_family = TRUE; - - _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) -{ - /* We assert here that we own font_face->family before casting - * away the const qualifer. */ - assert (font_face->owns_family); - free ((char*) font_face->family); -} - -static int -_cairo_toy_font_face_keys_equal (const void *key_a, - const void *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 && - face_a->weight == face_b->weight); -} - -/** - * cairo_toy_font_face_create: - * @family: a font family name, encoded in UTF-8 - * @slant: the slant for the font - * @weight: the weight for the font - * - * Creates a font face from a triplet of family, slant, and weight. - * These font faces are used in implementation of the the #cairo_t "toy" - * font API. - * - * If @family is the zero-length string "", the platform-specific default - * family is assumed. The default family then can be queried using - * cairo_toy_font_face_get_family(). - * - * The cairo_select_font_face() function uses this to create font faces. - * See that function for limitations of toy font faces. - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - * - * Since: 1.8 - **/ -cairo_font_face_t * -cairo_toy_font_face_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_status_t status; - cairo_toy_font_face_t key, *font_face; - cairo_hash_table_t *hash_table; - - if (family == NULL) - return (cairo_font_face_t*) &_cairo_font_face_null_pointer; - - /* Make sure we've got valid UTF-8 for the family */ - status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL); - if (status == CAIRO_STATUS_INVALID_STRING) - return (cairo_font_face_t*) &_cairo_font_face_invalid_string; - else if (status) - return (cairo_font_face_t*) &_cairo_font_face_nil; - - switch (slant) { - case CAIRO_FONT_SLANT_NORMAL: - case CAIRO_FONT_SLANT_ITALIC: - case CAIRO_FONT_SLANT_OBLIQUE: - break; - default: - return (cairo_font_face_t*) &_cairo_font_face_invalid_slant; - } - - switch (weight) { - case CAIRO_FONT_WEIGHT_NORMAL: - case CAIRO_FONT_WEIGHT_BOLD: - break; - default: - return (cairo_font_face_t*) &_cairo_font_face_invalid_weight; - } - - if (*family == '\0') - family = CAIRO_FONT_FAMILY_DEFAULT; - - hash_table = _cairo_toy_font_face_hash_table_lock (); - if (hash_table == NULL) - goto UNWIND; - - _cairo_toy_font_face_init_key (&key, family, slant, weight); - - /* Return existing font_face if it exists in the hash table. */ - if (_cairo_hash_table_lookup (hash_table, - &key.base.hash_entry, - (cairo_hash_entry_t **) &font_face)) - { - if (! font_face->base.status) { - /* We increment the reference count here manually to avoid - double-locking. */ - _cairo_reference_count_inc (&font_face->base.ref_count); - _cairo_toy_font_face_hash_table_unlock (); - return &font_face->base; - } - - /* remove the bad font from the hash table */ - _cairo_hash_table_remove (hash_table, &key.base.hash_entry); - font_face->base.hash_entry.hash = 0; - } - - /* Otherwise create it and insert into hash table. */ - font_face = malloc (sizeof (cairo_toy_font_face_t)); - if (font_face == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto UNWIND_HASH_TABLE_LOCK; - } - - status = _cairo_toy_font_face_init (font_face, family, slant, weight); - if (status) - goto UNWIND_FONT_FACE_MALLOC; - - status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); - if (status) - goto UNWIND_FONT_FACE_INIT; - - _cairo_toy_font_face_hash_table_unlock (); - - return &font_face->base; - - UNWIND_FONT_FACE_INIT: - _cairo_toy_font_face_fini (font_face); - UNWIND_FONT_FACE_MALLOC: - free (font_face); - UNWIND_HASH_TABLE_LOCK: - _cairo_toy_font_face_hash_table_unlock (); - UNWIND: - return (cairo_font_face_t*) &_cairo_font_face_nil; -} -slim_hidden_def (cairo_toy_font_face_create); - -static void -_cairo_toy_font_face_destroy (void *abstract_face) -{ - cairo_toy_font_face_t *font_face = abstract_face; - cairo_hash_table_t *hash_table; - - if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count)) - return; - - hash_table = _cairo_toy_font_face_hash_table_lock (); - /* All created objects must have been mapped in the hash table. */ - assert (hash_table != NULL); - - if (font_face->base.hash_entry.hash != 0) - _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); - - _cairo_toy_font_face_hash_table_unlock (); - - _cairo_toy_font_face_fini (font_face); -} - -static cairo_status_t -_cairo_toy_font_face_scaled_font_get_implementation (void *abstract_font_face, - cairo_font_face_t **font_face_out) -{ - cairo_toy_font_face_t *font_face = abstract_font_face; - cairo_status_t status; - - if (font_face->base.status) - return font_face->base.status; - - if (CAIRO_SCALED_FONT_BACKEND_DEFAULT != &_cairo_user_scaled_font_backend) - { - const cairo_scaled_font_backend_t * backend = CAIRO_SCALED_FONT_BACKEND_DEFAULT; - - if (backend->get_implementation == NULL) { - *font_face_out = &font_face->base; - return CAIRO_STATUS_SUCCESS; - } - - status = backend->get_implementation (font_face, - font_face_out); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_font_face_set_error (&font_face->base, status); - } - - status = _cairo_user_scaled_font_backend.get_implementation (font_face, - font_face_out); - - return _cairo_font_face_set_error (&font_face->base, status); -} - -static cairo_status_t -_cairo_toy_font_face_scaled_font_create (void *abstract_font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font) -{ - cairo_toy_font_face_t *font_face = abstract_font_face; - cairo_status_t status; - - if (font_face->base.status) - return font_face->base.status; - - status = cairo_font_options_status ((cairo_font_options_t *) options); - if (status) - return status; - - if (CAIRO_SCALED_FONT_BACKEND_DEFAULT != &_cairo_user_scaled_font_backend) - { - const cairo_scaled_font_backend_t * backend = CAIRO_SCALED_FONT_BACKEND_DEFAULT; - - *scaled_font = NULL; - status = backend->create_toy (font_face, - font_matrix, - ctm, - options, - scaled_font); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_font_face_set_error (&font_face->base, status); - - if (*scaled_font) - cairo_scaled_font_destroy (*scaled_font); - } - - status = _cairo_user_scaled_font_backend.create_toy (font_face, - font_matrix, - ctm, - options, - scaled_font); - - return _cairo_font_face_set_error (&font_face->base, status); -} - -static cairo_bool_t -_cairo_font_face_is_toy (cairo_font_face_t *font_face) -{ - return font_face->backend == &_cairo_toy_font_face_backend; -} - -/** - * cairo_toy_font_face_get_family: - * @font_face: A toy font face - * - * Gets the familly name of a toy font. - * - * Return value: The family name. This string is owned by the font face - * and remains valid as long as the font face is alive (referenced). - * - * Since: 1.8 - **/ -const char * -cairo_toy_font_face_get_family (cairo_font_face_t *font_face) -{ - cairo_toy_font_face_t *toy_font_face = (cairo_toy_font_face_t *) font_face; - if (! _cairo_font_face_is_toy (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return CAIRO_FONT_FAMILY_DEFAULT; - } - assert (toy_font_face->owns_family); - return toy_font_face->family; -} - -/** - * cairo_toy_font_face_get_slant: - * @font_face: A toy font face - * - * Gets the slant a toy font. - * - * Return value: The slant value - * - * Since: 1.8 - **/ -cairo_font_slant_t -cairo_toy_font_face_get_slant (cairo_font_face_t *font_face) -{ - cairo_toy_font_face_t *toy_font_face = (cairo_toy_font_face_t *) font_face; - if (! _cairo_font_face_is_toy (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return CAIRO_FONT_SLANT_DEFAULT; - } - return toy_font_face->slant; -} -slim_hidden_def (cairo_toy_font_face_get_slant); - -/** - * cairo_toy_font_face_get_weight: - * @font_face: A toy font face - * - * Gets the weight a toy font. - * - * Return value: The weight value - * - * Since: 1.8 - **/ -cairo_font_weight_t -cairo_toy_font_face_get_weight (cairo_font_face_t *font_face) -{ - cairo_toy_font_face_t *toy_font_face = (cairo_toy_font_face_t *) font_face; - if (! _cairo_font_face_is_toy (font_face)) { - if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) - return CAIRO_FONT_WEIGHT_DEFAULT; - } - return toy_font_face->weight; -} -slim_hidden_def (cairo_toy_font_face_get_weight); - -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_get_implementation, - _cairo_toy_font_face_scaled_font_create -}; - void _cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, const cairo_unscaled_font_backend_t *backend) @@ -760,17 +288,3 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) free (unscaled_font); } - -void -_cairo_font_face_reset_static_data (void) -{ - _cairo_scaled_font_map_destroy (); - - /* We manually acquire the lock rather than calling - * cairo_toy_font_face_hash_table_lock simply to avoid - * creating the table only to destroy it again. */ - CAIRO_MUTEX_LOCK (_cairo_font_face_mutex); - _cairo_hash_table_destroy (cairo_toy_font_face_hash_table); - cairo_toy_font_face_hash_table = NULL; - CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex); -} diff --git a/src/cairo-freelist.c b/src/cairo-freelist.c index 3887f4b..f6eb4fe 100644 --- a/src/cairo-freelist.c +++ b/src/cairo-freelist.c @@ -27,7 +27,7 @@ void _cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize) { - memset (freelist, 0, sizeof(cairo_freelist_t)); + memset (freelist, 0, sizeof (cairo_freelist_t)); freelist->nodesize = nodesize; } @@ -36,7 +36,11 @@ _cairo_freelist_fini (cairo_freelist_t *freelist) { cairo_freelist_node_t *node = freelist->first_free_node; while (node) { - cairo_freelist_node_t *next = node->next; + cairo_freelist_node_t *next; + + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + next = node->next; + free (node); node = next; } @@ -46,10 +50,16 @@ void * _cairo_freelist_alloc (cairo_freelist_t *freelist) { if (freelist->first_free_node) { - cairo_freelist_node_t *node = freelist->first_free_node; + cairo_freelist_node_t *node; + + node = freelist->first_free_node; + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); freelist->first_free_node = node->next; - return (void*)node; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize)); + + return node; } + return malloc (freelist->nodesize); } @@ -69,5 +79,6 @@ _cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode) if (node) { node->next = freelist->first_free_node; freelist->first_free_node = node; + VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize)); } } diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 0b1e624..2150a41 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -45,8 +45,10 @@ #include <float.h> +#if CAIRO_HAS_FC_FONT #include <fontconfig/fontconfig.h> #include <fontconfig/fcfreetype.h> +#endif #include <ft2build.h> #include FT_FREETYPE_H @@ -117,10 +119,6 @@ _cairo_ft_unscaled_font_keys_equal (const void *key_a, static void _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); -static cairo_status_t -_cairo_ft_font_options_substitute (const cairo_font_options_t *options, - FcPattern *pattern); - typedef enum _cairo_ft_extra_flags { CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0), CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1) @@ -134,13 +132,31 @@ typedef struct _cairo_ft_options { struct _cairo_ft_font_face { cairo_font_face_t base; + cairo_ft_unscaled_font_t *unscaled; cairo_ft_options_t ft_options; cairo_ft_font_face_t *next; + +#if CAIRO_HAS_FC_FONT + FcPattern *pattern; /* if pattern is set, the above fields will be NULL */ +#endif }; static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend; +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern); + +static cairo_font_face_t * +_cairo_ft_resolve_pattern (FcPattern *pattern, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); + +#endif + /* * We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t. * The hash table itself isn't limited in size. However, we limit the @@ -171,7 +187,7 @@ _font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map, } } -static void +static cairo_status_t _cairo_ft_unscaled_font_map_create (void) { cairo_ft_unscaled_font_map_t *font_map; @@ -182,63 +198,62 @@ _cairo_ft_unscaled_font_map_create (void) assert (cairo_ft_unscaled_font_map == NULL); font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t)); - if (font_map == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } + if (unlikely (font_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_map->hash_table = _cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal); - if (font_map->hash_table == NULL) + if (unlikely (font_map->hash_table == NULL)) goto FAIL; - if (FT_Init_FreeType (&font_map->ft_library)) + if (unlikely (FT_Init_FreeType (&font_map->ft_library))) goto FAIL; font_map->num_open_faces = 0; cairo_ft_unscaled_font_map = font_map; - return; + return CAIRO_STATUS_SUCCESS; FAIL: - if (font_map) { - if (font_map->hash_table) - _cairo_hash_table_destroy (font_map->hash_table); - free (font_map); - } - cairo_ft_unscaled_font_map = NULL; + if (font_map->hash_table) + _cairo_hash_table_destroy (font_map->hash_table); + free (font_map); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + + +static void +_cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure) +{ + cairo_ft_unscaled_font_t *unscaled = entry; + cairo_ft_unscaled_font_map_t *font_map = closure; + + _cairo_hash_table_remove (font_map->hash_table, + &unscaled->base.hash_entry); + + if (! unscaled->from_face) + _font_map_release_face_lock_held (font_map, unscaled); + + _cairo_ft_unscaled_font_fini (unscaled); + free (unscaled); } static void _cairo_ft_unscaled_font_map_destroy (void) { - cairo_ft_unscaled_font_t *unscaled; cairo_ft_unscaled_font_map_t *font_map; CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); + font_map = cairo_ft_unscaled_font_map; + cairo_ft_unscaled_font_map = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); - if (cairo_ft_unscaled_font_map) { - font_map = cairo_ft_unscaled_font_map; - - /* This is rather inefficient, but destroying the hash table - * is something we only do during debugging, (during - * cairo_debug_reset_static_data), when efficiency is not - * relevant. */ - while (1) { - unscaled = _cairo_hash_table_random_entry (font_map->hash_table, - NULL); - if (unscaled == NULL) - break; - _cairo_hash_table_remove (font_map->hash_table, - &unscaled->base.hash_entry); - - if (! unscaled->from_face) - _font_map_release_face_lock_held (font_map, unscaled); - _cairo_ft_unscaled_font_fini (unscaled); - free (unscaled); - } - + if (font_map != NULL) { + _cairo_hash_table_foreach (font_map->hash_table, + _cairo_ft_unscaled_font_map_pluck_entry, + font_map); assert (font_map->num_open_faces == 0); FT_Done_FreeType (font_map->ft_library); @@ -246,11 +261,7 @@ _cairo_ft_unscaled_font_map_destroy (void) _cairo_hash_table_destroy (font_map->hash_table); free (font_map); - - cairo_ft_unscaled_font_map = NULL; } - - CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); } static cairo_ft_unscaled_font_map_t * @@ -258,13 +269,9 @@ _cairo_ft_unscaled_font_map_lock (void) { CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); - if (cairo_ft_unscaled_font_map == NULL) - { - _cairo_ft_unscaled_font_map_create (); - - if (cairo_ft_unscaled_font_map == NULL) { + if (unlikely (cairo_ft_unscaled_font_map == NULL)) { + if (unlikely (_cairo_ft_unscaled_font_map_create ())) { CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } } @@ -341,8 +348,9 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled, unscaled->face = NULL; filename_copy = strdup (filename); - if (filename_copy == NULL) + if (unlikely (filename_copy == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL); } @@ -406,50 +414,52 @@ _cairo_ft_unscaled_font_keys_equal (const void *key_a, /* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from * pattern. Returns a new reference to the unscaled font. */ -static cairo_ft_unscaled_font_t * +static cairo_status_t _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, char *filename, int id, - FT_Face font_face) + FT_Face font_face, + cairo_ft_unscaled_font_t **out) { cairo_ft_unscaled_font_t key, *unscaled; cairo_ft_unscaled_font_map_t *font_map; cairo_status_t status; font_map = _cairo_ft_unscaled_font_map_lock (); - if (font_map == NULL) - goto UNWIND; + if (unlikely (font_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face); /* Return existing unscaled font if it exists in the hash table. */ - if (_cairo_hash_table_lookup (font_map->hash_table, &key.base.hash_entry, - (cairo_hash_entry_t **) &unscaled)) - { + unscaled = _cairo_hash_table_lookup (font_map->hash_table, + &key.base.hash_entry); + if (unscaled != NULL) { _cairo_unscaled_font_reference (&unscaled->base); - _cairo_ft_unscaled_font_map_unlock (); - return unscaled; + goto DONE; } /* Otherwise create it and insert into hash table. */ unscaled = malloc (sizeof (cairo_ft_unscaled_font_t)); - if (unscaled == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + if (unlikely (unscaled == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNWIND_FONT_MAP_LOCK; } status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face); - if (status) + if (unlikely (status)) goto UNWIND_UNSCALED_MALLOC; + assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash); status = _cairo_hash_table_insert (font_map->hash_table, &unscaled->base.hash_entry); - if (status) + if (unlikely (status)) goto UNWIND_UNSCALED_FONT_INIT; +DONE: _cairo_ft_unscaled_font_map_unlock (); - - return unscaled; + *out = unscaled; + return CAIRO_STATUS_SUCCESS; UNWIND_UNSCALED_FONT_INIT: _cairo_ft_unscaled_font_fini (unscaled); @@ -457,39 +467,54 @@ UNWIND_UNSCALED_MALLOC: free (unscaled); UNWIND_FONT_MAP_LOCK: _cairo_ft_unscaled_font_map_unlock (); -UNWIND: - return NULL; + return status; } -static cairo_ft_unscaled_font_t * -_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern) +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, + cairo_ft_unscaled_font_t **out) { FT_Face font_face = NULL; char *filename = NULL; int id = 0; + FcResult ret; - if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face) != FcResultMatch) { - FcChar8 *fc_filename = NULL; + ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face); + if (ret == FcResultMatch) + goto DONE; + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (FcPatternGetString (pattern, FC_FILE, 0, &fc_filename) != FcResultMatch) - goto UNWIND; - filename = (char *) fc_filename; + ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename); + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (ret == FcResultMatch) { + /* If FC_INDEX is not set, we just use 0 */ + ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id); + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (FcPatternGetInteger (pattern, FC_INDEX, 0, &id) != FcResultMatch) - goto UNWIND; + goto DONE; } - return _cairo_ft_unscaled_font_create_internal (font_face != NULL, filename, id, font_face); + /* The pattern contains neither a face nor a filename, resolve it later. */ + *out = NULL; + return CAIRO_STATUS_SUCCESS; -UNWIND: - return NULL; +DONE: + return _cairo_ft_unscaled_font_create_internal (font_face != NULL, + filename, id, font_face, + out); } +#endif -static cairo_ft_unscaled_font_t * -_cairo_ft_unscaled_font_create_from_face (FT_Face face) +static cairo_status_t +_cairo_ft_unscaled_font_create_from_face (FT_Face face, + cairo_ft_unscaled_font_t **out) { - return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face); + return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out); } static void @@ -505,6 +530,12 @@ _cairo_ft_unscaled_font_destroy (void *abstract_font) /* All created objects must have been mapped in the font map. */ assert (font_map != NULL); + if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_ft_unscaled_font_map_unlock (); + return; + } + _cairo_hash_table_remove (font_map->hash_table, &unscaled->base.hash_entry); @@ -527,9 +558,9 @@ _cairo_ft_unscaled_font_destroy (void *abstract_font) } static cairo_bool_t -_has_unlocked_face (void *entry) +_has_unlocked_face (const void *entry) { - cairo_ft_unscaled_font_t *unscaled = entry; + const cairo_ft_unscaled_font_t *unscaled = entry; return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face); } @@ -624,7 +655,7 @@ _compute_transform (cairo_ft_font_transform_t *sf, status = _cairo_matrix_compute_basis_scale_factors (scale, &x_scale, &y_scale, 1); - if (status) + if (unlikely (status)) return status; /* FreeType docs say this about x_scale and y_scale: @@ -674,7 +705,7 @@ _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, unscaled->current_scale = *scale; status = _compute_transform (&sf, scale); - if (status) + if (unlikely (status)) return status; unscaled->x_scale = sf.x_scale; @@ -877,7 +908,7 @@ _get_bitmap_surface (FT_Bitmap *bitmap, stride = bitmap->pitch; stride_rgba = (width_rgba * 4 + 3) & ~3; data_rgba = calloc (stride_rgba, height); - if (data_rgba == NULL) { + if (unlikely (data_rgba == NULL)) { if (own_buffer) free (bitmap->buffer); return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -965,6 +996,8 @@ _get_bitmap_surface (FT_Bitmap *bitmap, _cairo_image_surface_assume_ownership_of_data ((*surface)); + _cairo_debug_check_image_surface_is_defined (&(*surface)->base); + return CAIRO_STATUS_SUCCESS; } @@ -1076,7 +1109,7 @@ _render_glyph_outline (FT_Face face, bitmap.width = width * hmul; bitmap.rows = height * vmul; bitmap.buffer = calloc (stride, bitmap.rows); - if (bitmap.buffer == NULL) + if (unlikely (bitmap.buffer == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); FT_Outline_Translate (outline, -cbox.xMin*hmul, -cbox.yMin*vmul); @@ -1087,7 +1120,7 @@ _render_glyph_outline (FT_Face face, } status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface); - if (status) + if (unlikely (status)) return status; } @@ -1128,7 +1161,7 @@ _render_glyph_bitmap (FT_Face face, return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _get_bitmap_surface (&glyphslot->bitmap, FALSE, font_options, surface); - if (status) + if (unlikely (status)) return status; /* @@ -1215,13 +1248,13 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, transformed_to_original = original_to_transformed; status = cairo_matrix_invert (&transformed_to_original); - if (status) + if (unlikely (status)) return status; /* We need to pad out the width to 32-bit intervals for cairo-xlib-surface.c */ width = (width + 3) & ~3; image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); - if (image->status) + if (unlikely (image->status)) return image->status; /* Initialize it to empty @@ -1230,7 +1263,7 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, CAIRO_COLOR_TRANSPARENT, 0, 0, width, height); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (image); return status; } @@ -1248,7 +1281,7 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, _cairo_pattern_fini (&pattern.base); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (image); return status; } @@ -1284,8 +1317,9 @@ typedef struct _cairo_ft_scaled_font { cairo_ft_options_t ft_options; } cairo_ft_scaled_font_t; -const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend; +static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend; +#if CAIRO_HAS_FC_FONT /* The load flags passed to FT_Load_Glyph control aspects like hinting and * antialiasing. Here we compute them from the fields of a FcPattern. */ @@ -1421,6 +1455,7 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) *ret = ft_options; } +#endif static void _cairo_ft_options_merge (cairo_ft_options_t *options, @@ -1496,52 +1531,54 @@ _cairo_ft_options_merge (cairo_ft_options_t *options, } static cairo_status_t -_cairo_ft_scaled_font_create (cairo_ft_unscaled_font_t *unscaled, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_ft_options_t ft_options, - cairo_scaled_font_t **font_out) +_cairo_ft_font_face_scaled_font_create (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font_out) { + cairo_ft_font_face_t *font_face = abstract_font_face; cairo_ft_scaled_font_t *scaled_font; FT_Face face; FT_Size_Metrics *metrics; cairo_font_extents_t fs_metrics; cairo_status_t status; + cairo_ft_unscaled_font_t *unscaled; - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) + assert (font_face->unscaled); + + face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled); + if (unlikely (face == NULL)) /* backend error */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); - scaled_font = malloc (sizeof(cairo_ft_scaled_font_t)); - if (scaled_font == NULL) { + scaled_font = malloc (sizeof (cairo_ft_scaled_font_t)); + if (unlikely (scaled_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } + scaled_font->unscaled = unscaled = font_face->unscaled; _cairo_unscaled_font_reference (&unscaled->base); - scaled_font->unscaled = unscaled; _cairo_font_options_init_copy (&scaled_font->ft_options.base, options); - _cairo_ft_options_merge (&scaled_font->ft_options, &ft_options); + _cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options); status = _cairo_scaled_font_init (&scaled_font->base, - font_face, + &font_face->base, font_matrix, ctm, options, &_cairo_ft_scaled_font_backend); - if (status) { - _cairo_unscaled_font_destroy (&unscaled->base); - free (scaled_font); - goto FAIL; - } + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; status = _cairo_ft_unscaled_font_set_scale (unscaled, &scaled_font->base.scale); - if (status) { + if (unlikely (status)) { + /* This can only fail if we encounter an error with the underlying + * font, so propagate the error back to the font-face. */ + _cairo_ft_unscaled_font_unlock_face (unscaled); _cairo_unscaled_font_destroy (&unscaled->base); free (scaled_font); - goto FAIL; + return status; } @@ -1595,13 +1632,21 @@ _cairo_ft_scaled_font_create (cairo_ft_unscaled_font_t *unscaled, } status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; - *font_out = &scaled_font->base; - - FAIL: _cairo_ft_unscaled_font_unlock_face (unscaled); - return status; + *font_out = &scaled_font->base; + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SCALED_FONT: + _cairo_unscaled_font_destroy (&unscaled->base); + free (scaled_font); + FAIL: + _cairo_ft_unscaled_font_unlock_face (font_face->unscaled); + *font_out = _cairo_scaled_font_create_in_error (status); + return CAIRO_STATUS_SUCCESS; /* non-backend error */ } cairo_bool_t @@ -1610,121 +1655,6 @@ _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font) return scaled_font->backend == &_cairo_ft_scaled_font_backend; } -static cairo_status_t -_cairo_ft_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *font_options, - cairo_scaled_font_t **font) -{ - FcPattern *pattern, *resolved; - cairo_ft_unscaled_font_t *unscaled; - FcResult result; - int fcslant; - int fcweight; - cairo_matrix_t scale; - cairo_status_t status; - cairo_ft_font_transform_t sf; - cairo_ft_options_t ft_options; - - cairo_matrix_multiply (&scale, font_matrix, ctm); - status = _compute_transform (&sf, &scale); - if (status) - return status; - - pattern = FcPatternCreate (); - if (!pattern) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (!FcPatternAddString (pattern, - FC_FAMILY, (unsigned char *) toy_face->family)) - { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - switch (toy_face->slant) - { - case CAIRO_FONT_SLANT_ITALIC: - fcslant = FC_SLANT_ITALIC; - break; - case CAIRO_FONT_SLANT_OBLIQUE: - fcslant = FC_SLANT_OBLIQUE; - break; - case CAIRO_FONT_SLANT_NORMAL: - default: - fcslant = FC_SLANT_ROMAN; - break; - } - - if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - switch (toy_face->weight) - { - case CAIRO_FONT_WEIGHT_BOLD: - fcweight = FC_WEIGHT_BOLD; - break; - case CAIRO_FONT_WEIGHT_NORMAL: - default: - fcweight = FC_WEIGHT_MEDIUM; - break; - } - - if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - status = _cairo_ft_font_options_substitute (font_options, pattern); - if (status) - goto FREE_PATTERN; - - FcDefaultSubstitute (pattern); - - resolved = FcFontMatch (NULL, pattern, &result); - if (!resolved) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_PATTERN; - } - - unscaled = _cairo_ft_unscaled_font_create_for_pattern (resolved); - if (!unscaled) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FREE_RESOLVED; - } - - _get_pattern_ft_options (resolved, &ft_options); - - status = _cairo_ft_scaled_font_create (unscaled, - &toy_face->base, - font_matrix, ctm, - font_options, ft_options, - font); - - _cairo_unscaled_font_destroy (&unscaled->base); - - FREE_RESOLVED: - FcPatternDestroy (resolved); - - FREE_PATTERN: - FcPatternDestroy (pattern); - - return status; -} - static void _cairo_ft_scaled_font_fini (void *abstract_font) { @@ -1866,7 +1796,7 @@ _decompose_glyph_outline (FT_Face face, } status = _cairo_path_fixed_close_path (path); - if (status) { + if (unlikely (status)) { _cairo_path_fixed_destroy (path); return status; } @@ -1921,7 +1851,7 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, &scaled_font->base.scale); - if (status) + if (unlikely (status)) goto FAIL; /* Ignore global advance unconditionally */ @@ -2069,14 +1999,16 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, } else { status = _render_glyph_bitmap (face, &scaled_font->ft_options.base, &surface); - if (status == CAIRO_STATUS_SUCCESS && unscaled->have_shape) { + if (likely (status == CAIRO_STATUS_SUCCESS) && + unscaled->have_shape) + { status = _transform_glyph_bitmap (&unscaled->current_shape, &surface); - if (status) + if (unlikely (status)) cairo_surface_destroy (&surface->base); } } - if (status) + if (unlikely (status)) goto FAIL; _cairo_scaled_glyph_set_surface (scaled_glyph, @@ -2118,7 +2050,7 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, else status = CAIRO_INT_STATUS_UNSUPPORTED; - if (status) + if (unlikely (status)) goto FAIL; _cairo_scaled_glyph_set_path (scaled_glyph, @@ -2144,9 +2076,11 @@ _cairo_ft_ucs4_to_index (void *abstract_font, if (!face) return 0; - /* If making this compile without fontconfig, use: - * index = FT_Get_Char_Index (face, ucs4); */ +#if CAIRO_HAS_FC_FONT index = FcFreeTypeCharIndex (face, ucs4); +#else + index = FT_Get_Char_Index (face, ucs4); +#endif _cairo_ft_unscaled_font_unlock_face (unscaled); return index; @@ -2212,10 +2146,8 @@ _cairo_ft_index_to_ucs4(void *abstract_font, return CAIRO_STATUS_SUCCESS; } -const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { +static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { CAIRO_FONT_TYPE_FT, - NULL, - _cairo_ft_scaled_font_create_toy, _cairo_ft_scaled_font_fini, _cairo_ft_scaled_glyph_init, NULL, /* text_to_glyphs */ @@ -2227,6 +2159,75 @@ const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { /* #cairo_ft_font_face_t */ +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, + cairo_font_face_t **out); + +static cairo_status_t +_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + FcPattern *pattern; + int fcslant; + int fcweight; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + pattern = FcPatternCreate (); + if (!pattern) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (!FcPatternAddString (pattern, + FC_FAMILY, (unsigned char *) toy_face->family)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + switch (toy_face->slant) + { + case CAIRO_FONT_SLANT_ITALIC: + fcslant = FC_SLANT_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + fcslant = FC_SLANT_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + fcslant = FC_SLANT_ROMAN; + break; + } + + if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + switch (toy_face->weight) + { + case CAIRO_FONT_WEIGHT_BOLD: + fcweight = FC_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + fcweight = FC_WEIGHT_MEDIUM; + break; + } + + if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + status = _cairo_ft_font_face_create_for_pattern (pattern, font_face); + + FREE_PATTERN: + FcPatternDestroy (pattern); + + return status; +} +#endif + static void _cairo_ft_font_face_destroy (void *abstract_face) { @@ -2286,17 +2287,20 @@ _cairo_ft_font_face_destroy (void *abstract_face) _cairo_unscaled_font_destroy (&font_face->unscaled->base); font_face->unscaled = NULL; } + +#if CAIRO_HAS_FC_FONT + if (font_face->pattern) + FcPatternDestroy (font_face->pattern); +#endif } -static cairo_status_t -_cairo_ft_font_face_scaled_font_create (void *abstract_face, +static cairo_font_face_t * +_cairo_ft_font_face_get_implementation (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font) + const cairo_font_options_t *options) { - cairo_ft_font_face_t *font_face = abstract_face; - cairo_ft_options_t ft_options; + cairo_ft_font_face_t *font_face = abstract_face; /* The handling of font options is different depending on how the * font face was created. When the user creates a font face with @@ -2306,28 +2310,62 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face, * derived from a pattern where the user has called * cairo_ft_font_options_substitute(), so *just* use those load * flags and ignore the options. - * - * XXX two points about the above comment: - * 1. I don't see how the comment is relevant here, - * 2. What if the face is coming from FC_FT_FACE of a pattern? */ - ft_options = font_face->ft_options; +#if CAIRO_HAS_FC_FONT + /* If we have an unresolved pattern, resolve it and create + * unscaled font. Otherwise, use the ones stored in font_face. + */ + if (font_face->pattern) { + return _cairo_ft_resolve_pattern (font_face->pattern, + font_matrix, + ctm, + options); + } +#endif - return _cairo_ft_scaled_font_create (font_face->unscaled, - &font_face->base, - font_matrix, ctm, - options, ft_options, - scaled_font); + return abstract_face; } -static const cairo_font_face_backend_t _cairo_ft_font_face_backend = { +const cairo_font_face_backend_t _cairo_ft_font_face_backend = { CAIRO_FONT_TYPE_FT, +#if CAIRO_HAS_FC_FONT + _cairo_ft_font_face_create_for_toy, +#else + NULL, +#endif _cairo_ft_font_face_destroy, - NULL, /* direct implementation */ - _cairo_ft_font_face_scaled_font_create + _cairo_ft_font_face_scaled_font_create, + _cairo_ft_font_face_get_implementation }; +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, + cairo_font_face_t **out) +{ + cairo_ft_font_face_t *font_face; + + font_face = malloc (sizeof (cairo_ft_font_face_t)); + if (unlikely (font_face == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_face->unscaled = NULL; + font_face->next = NULL; + + font_face->pattern = FcPatternDuplicate (pattern); + if (unlikely (font_face->pattern == NULL)) { + free (font_face); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); + + *out = &font_face->base; + return CAIRO_STATUS_SUCCESS; +} +#endif + static cairo_font_face_t * _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, cairo_ft_options_t *ft_options) @@ -2363,7 +2401,7 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, /* No match found, create a new one */ font_face = malloc (sizeof (cairo_ft_font_face_t)); - if (!font_face) { + if (unlikely (!font_face)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } @@ -2384,6 +2422,10 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, font_face->next = unscaled->faces; unscaled->faces = font_face; +#if CAIRO_HAS_FC_FONT + font_face->pattern = NULL; +#endif + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); return &font_face->base; @@ -2391,6 +2433,7 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, /* implement the platform-specific interface */ +#if CAIRO_HAS_FC_FONT static cairo_status_t _cairo_ft_font_options_substitute (const cairo_font_options_t *options, FcPattern *pattern) @@ -2508,15 +2551,85 @@ cairo_ft_font_options_substitute (const cairo_font_options_t *options, _cairo_ft_font_options_substitute (options, pattern); } +static cairo_font_face_t * +_cairo_ft_resolve_pattern (FcPattern *pattern, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *font_options) +{ + cairo_status_t status; + + cairo_matrix_t scale; + FcPattern *resolved; + cairo_ft_font_transform_t sf; + FcResult result; + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; + cairo_font_face_t *font_face; + + scale = *ctm; + scale.x0 = scale.y0 = 0; + cairo_matrix_multiply (&scale, + font_matrix, + &scale); + + status = _compute_transform (&sf, &scale); + if (unlikely (status)) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + pattern = FcPatternDuplicate (pattern); + if (pattern == NULL) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + status = _cairo_ft_font_options_substitute (font_options, pattern); + if (status) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + FcDefaultSubstitute (pattern); + + resolved = FcFontMatch (NULL, pattern, &result); + if (!resolved) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled); + if (unlikely (status)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_RESOLVED; + } + + assert (unscaled != NULL); + + _get_pattern_ft_options (resolved, &ft_options); + font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_unscaled_font_destroy (&unscaled->base); + +FREE_RESOLVED: + FcPatternDestroy (resolved); + +FREE_PATTERN: + FcPatternDestroy (pattern); + + return font_face; +} + /** * cairo_ft_font_face_create_for_pattern: - * @pattern: A fully resolved fontconfig - * pattern. A pattern can be resolved, by, among other things, calling - * FcConfigSubstitute(), FcDefaultSubstitute(), then - * FcFontMatch(). Cairo will call FcPatternReference() on this - * pattern, so you should not further modify the pattern, but you can - * release your reference to the pattern with FcPatternDestroy() if - * you no longer need to access it. + * @pattern: A fontconfig pattern. Cairo makes a copy of the pattern + * if it needs to. You are free to modify or free @pattern after this call. * * Creates a new font face for the FreeType font backend based on a * fontconfig pattern. This font can then be used with @@ -2533,8 +2646,13 @@ cairo_ft_font_options_substitute (const cairo_font_options_t *options, * * The pattern's FC_FT_FACE element is inspected first and if that is set, * that will be the FreeType font face associated with the returned cairo - * font face. Otherwise the FC_FILE and FC_INDEX elements of @pattern are - * used to load a font face from file. + * font face. Otherwise the FC_FILE element is checked. If it's set, + * that and the value of the FC_INDEX element (defaults to zero) of @pattern + * are used to load a font face from file. + * + * If both steps from the previous paragraph fails, @pattern will be passed + * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch, + * and the resulting font pattern is used. * * If the FC_FT_FACE element of @pattern is set, the user is responsible * for making sure that the referenced FT_Face remains valid for the life @@ -2551,11 +2669,20 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern) cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; + cairo_status_t status; - unscaled = _cairo_ft_unscaled_font_create_for_pattern (pattern); - if (unscaled == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; + status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); + if (unlikely (status)) + return (cairo_font_face_t *) &_cairo_font_face_nil; + if (unlikely (unscaled == NULL)) { + /* Store the pattern. We will resolve it and create unscaled + * font when creating scaled fonts */ + status = _cairo_ft_font_face_create_for_pattern (pattern, + &font_face); + if (unlikely (status)) + return (cairo_font_face_t *) &_cairo_font_face_nil; + + return font_face; } _get_pattern_ft_options (pattern, &ft_options); @@ -2564,6 +2691,7 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern) return font_face; } +#endif /** * cairo_ft_font_face_create_for_ft_face: @@ -2618,12 +2746,11 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; + cairo_status_t status; - unscaled = _cairo_ft_unscaled_font_create_from_face (face); - if (unscaled == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled); + if (unlikely (status)) return (cairo_font_face_t *)&_cairo_font_face_nil; - } ft_options.load_flags = load_flags; ft_options.extra_flags = 0; @@ -2656,7 +2783,7 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, * threaded application, because freetype's design makes it unsafe to * call freetype functions simultaneously from multiple threads, (even * if using distinct FT_Face objects). Because of this, application - * code that acquires an FT_Face object with this call must add it's + * code that acquires an FT_Face object with this call must add its * own locking to protect any use of that object, (and which also must * protect any other calls into cairo as almost any cairo function * might result in a call into the freetype library). @@ -2681,14 +2808,14 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) return NULL; face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled); - if (face == NULL) { + if (unlikely (face == NULL)) { status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY); return NULL; } status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, &scaled_font->base.scale); - if (status) { + if (unlikely (status)) { _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); status = _cairo_scaled_font_set_error (&scaled_font->base, status); return NULL; @@ -2762,6 +2889,18 @@ _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) return FALSE; } +unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_scaled_font_t *ft_scaled_font; + + if (! _cairo_scaled_font_is_ft (scaled_font)) + return 0; + + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; + return ft_scaled_font->ft_options.load_flags; +} + void _cairo_ft_font_reset_static_data (void) { diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h index 3e7d3de..00f7f77 100644 --- a/src/cairo-ft-private.h +++ b/src/cairo-ft-private.h @@ -64,6 +64,9 @@ _cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); cairo_private cairo_bool_t _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); +cairo_private unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); + CAIRO_END_DECLS #endif /* CAIRO_HAS_FT_FONT */ diff --git a/src/cairo-ft.h b/src/cairo-ft.h index 91e2db8..b7178d3 100644 --- a/src/cairo-ft.h +++ b/src/cairo-ft.h @@ -43,18 +43,14 @@ /* Fontconfig/Freetype platform-specific font interface */ -#include <fontconfig/fontconfig.h> #include <ft2build.h> #include FT_FREETYPE_H -CAIRO_BEGIN_DECLS - -cairo_public cairo_font_face_t * -cairo_ft_font_face_create_for_pattern (FcPattern *pattern); +#if CAIRO_HAS_FC_FONT +#include <fontconfig/fontconfig.h> +#endif -cairo_public void -cairo_ft_font_options_substitute (const cairo_font_options_t *options, - FcPattern *pattern); +CAIRO_BEGIN_DECLS cairo_public cairo_font_face_t * cairo_ft_font_face_create_for_ft_face (FT_Face face, @@ -66,6 +62,17 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font); cairo_public void cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); +#if CAIRO_HAS_FC_FONT + +cairo_public cairo_font_face_t * +cairo_ft_font_face_create_for_pattern (FcPattern *pattern); + +cairo_public void +cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern); + +#endif + CAIRO_END_DECLS #else /* CAIRO_HAS_FT_FONT */ diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c index 784bff9..cd0d254 100644 --- a/src/cairo-glitz-surface.c +++ b/src/cairo-glitz-surface.c @@ -33,8 +33,10 @@ typedef struct _cairo_glitz_surface { glitz_surface_t *surface; glitz_format_t *format; - cairo_bool_t has_clip; - cairo_region_t clip; + + cairo_bool_t has_clip; + glitz_box_t *clip_boxes; + int num_clip_boxes; } cairo_glitz_surface_t; static const cairo_surface_backend_t * @@ -45,10 +47,8 @@ _cairo_glitz_surface_finish (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; - if (surface->has_clip) { - glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - _cairo_region_fini (&surface->clip); - } + if (surface->clip_boxes) + free (surface->clip_boxes); glitz_surface_destroy (surface->surface); @@ -89,7 +89,7 @@ _cairo_glitz_surface_create_similar (void *abstract_src, glitz_find_standard_format (drawable, _glitz_format_from_content (content)); if (!gformat) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + return NULL; surface = glitz_surface_create (drawable, gformat, width <= 0 ? 1 : width, @@ -106,78 +106,40 @@ _cairo_glitz_surface_create_similar (void *abstract_src, return crsurface; } -static cairo_bool_t -_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format) -{ - switch (masks->bpp) { - case 32: - if (masks->alpha_mask == 0xff000000 && - masks->red_mask == 0x00ff0000 && - masks->green_mask == 0x0000ff00 && - masks->blue_mask == 0x000000ff) - { - *format = CAIRO_FORMAT_ARGB32; - return TRUE; - } - if (masks->alpha_mask == 0x00000000 && - masks->red_mask == 0x00ff0000 && - masks->green_mask == 0x0000ff00 && - masks->blue_mask == 0x000000ff) - { - *format = CAIRO_FORMAT_RGB24; - return TRUE; - } - break; - case 8: - if (masks->alpha_mask == 0xff) - { - *format = CAIRO_FORMAT_A8; - return TRUE; - } - break; - case 1: - if (masks->alpha_mask == 0x1) - { - *format = CAIRO_FORMAT_A1; - return TRUE; - } - break; - } - return FALSE; -} - static cairo_status_t -_cairo_glitz_get_boxes_from_region (cairo_region_t *region, glitz_box_t **boxes, int *nboxes) +_cairo_glitz_get_boxes_from_region (cairo_region_t *region, + glitz_box_t **boxes, + int *nboxes) { - cairo_box_int_t *cboxes; - cairo_status_t status; - int n, i; + pixman_box32_t *pboxes; + cairo_status_t status = CAIRO_STATUS_SUCCESS; - status = _cairo_region_get_boxes (region, &n, &cboxes); - if (status) - return status; + int n, i; + n = 0; + pboxes = pixman_region32_rectangles (®ion->rgn, &n); if (n == 0) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto done; + *nboxes = 0; + return CAIRO_STATUS_SUCCESS; } - *boxes = _cairo_malloc_ab (n, sizeof(glitz_box_t)); - if (*boxes == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto done; + if (n > *nboxes) { + *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); + if (*boxes == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto done; + } } for (i = 0; i < n; i++) { - (*boxes)[i].x1 = cboxes[i].p1.x; - (*boxes)[i].y1 = cboxes[i].p1.y; - (*boxes)[i].x2 = cboxes[i].p2.x; - (*boxes)[i].y2 = cboxes[i].p2.y; + (*boxes)[i].x1 = pboxes[i].x1; + (*boxes)[i].y1 = pboxes[i].y1; + (*boxes)[i].x2 = pboxes[i].x2; + (*boxes)[i].y2 = pboxes[i].y2; } *nboxes = n; done: - _cairo_region_boxes_fini (region, cboxes); return status; } @@ -188,94 +150,62 @@ _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, cairo_rectangle_int_t *rect_out) { cairo_image_surface_t *image; - int x1, y1, x2, y2; - int width, height; - unsigned char *pixels; + cairo_rectangle_int_t extents; + cairo_format_t format; cairo_format_masks_t masks; glitz_buffer_t *buffer; glitz_pixel_format_t pf; - cairo_format_t format; - x1 = 0; - y1 = 0; - x2 = glitz_surface_get_width (surface->surface); - y2 = glitz_surface_get_height (surface->surface); + extents.x = 0; + extents.y = 0; + extents.width = glitz_surface_get_width (surface->surface); + extents.height = glitz_surface_get_height (surface->surface); - if (interest) - { - if (interest->x > x1) - x1 = interest->x; - if (interest->y > y1) - y1 = interest->y; - if (interest->x + interest->width < x2) - x2 = interest->x + interest->width; - if (interest->y + interest->height < y2) - y2 = interest->y + interest->height; - - if (x1 >= x2 || y1 >= y2) - { + if (interest != NULL) { + if (! _cairo_rectangle_intersect (&extents, interest)) { *image_out = NULL; return CAIRO_STATUS_SUCCESS; } } - width = x2 - x1; - height = y2 - y1; - - if (rect_out) - { - rect_out->x = x1; - rect_out->y = y1; - rect_out->width = width; - rect_out->height = height; - } + if (rect_out != NULL) + *rect_out = extents; if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) { if (surface->format->color.red_size > 0) { - masks.bpp = 32; - if (surface->format->color.alpha_size > 0) - masks.alpha_mask = 0xff000000; + format = CAIRO_FORMAT_ARGB32; else - masks.alpha_mask = 0x0; - - masks.red_mask = 0xff0000; - masks.green_mask = 0xff00; - masks.blue_mask = 0xff; + format = CAIRO_FORMAT_RGB24; } else { - masks.bpp = 8; - masks.blue_mask = masks.green_mask = masks.red_mask = 0x0; - masks.alpha_mask = 0xff; + format = CAIRO_FORMAT_A8; } - } else { - masks.bpp = 32; - masks.alpha_mask = 0xff000000; - masks.red_mask = 0xff0000; - masks.green_mask = 0xff00; - masks.blue_mask = 0xff; - } + } else + format = CAIRO_FORMAT_ARGB32; + image = (cairo_image_surface_t*) + cairo_image_surface_create (format, extents.width, extents.height); + if (image->base.status) + return image->base.status; + + _pixman_format_to_masks (image->pixman_format, &masks); pf.fourcc = GLITZ_FOURCC_RGB; pf.masks.bpp = masks.bpp; pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; + pf.masks.red_mask = masks.red_mask; pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; + pf.masks.blue_mask = masks.blue_mask; pf.xoffset = 0; pf.skip_lines = 0; /* XXX: we should eventually return images with negative stride, need to verify that libpixman have no problem with this first. */ - pf.bytes_per_line = (((width * masks.bpp) / 8) + 3) & -4; + pf.bytes_per_line = image->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - pixels = _cairo_malloc_ab (height, pf.bytes_per_line); - if (!pixels) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - buffer = glitz_buffer_create_for_data (pixels); - if (!buffer) { - free (pixels); + buffer = glitz_buffer_create_for_data (image->data); + if (buffer == NULL) { + cairo_surface_destroy (&image->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -285,8 +215,8 @@ _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, 0, 0, NULL, 0); glitz_get_pixels (surface->surface, - x1, y1, - width, height, + extents.x, extents.y, + extents.width, extents.height, &pf, buffer); @@ -294,97 +224,15 @@ _cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, /* restore the clip, if any */ if (surface->has_clip) { - glitz_box_t *box; - cairo_status_t status; - int n; - - status = _cairo_glitz_get_boxes_from_region (&surface->clip, &box, &n); - if (status) { - free (pixels); - return status; - } - - glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); - - free (box); - } - - /* - * Prefer to use a standard pixman format instead of the - * general masks case. - */ - if (_CAIRO_MASK_FORMAT (&masks, &format)) { - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (pixels, - format, - x2 - x1, - y2 - y1, - pf.bytes_per_line); - if (image->base.status) - goto FAIL; - } else { - /* - * XXX This can't work. We must convert the data to one of the - * supported pixman formats. Pixman needs another function - * which takes data in an arbitrary format and converts it - * to something supported by that library. - */ - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_masks (pixels, - &masks, - x2 - x1, - y2 - y1, - pf.bytes_per_line); - if (image->base.status) - goto FAIL; + glitz_surface_set_clip_region (surface->surface, + 0, 0, + surface->clip_boxes, + surface->num_clip_boxes); } - _cairo_image_surface_assume_ownership_of_data (image); - *image_out = image; return CAIRO_STATUS_SUCCESS; - -FAIL: - free (pixels); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); -} - -static void -cairo_format_get_masks (cairo_format_t format, - uint32_t *bpp, - uint32_t *alpha, - uint32_t *red, - uint32_t *green, - uint32_t *blue) -{ - *red = 0x0; - *green = 0x0; - *blue = 0x0; - *alpha = 0x0; - - switch (format) - { - case CAIRO_FORMAT_ARGB32: - *alpha = 0xff000000; - case CAIRO_FORMAT_RGB24: - default: - *bpp = 32; - *red = 0x00ff0000; - *green = 0x0000ff00; - *blue = 0x000000ff; - break; - - case CAIRO_FORMAT_A8: - *bpp = 8; - *alpha = 0xff; - break; - - case CAIRO_FORMAT_A1: - *bpp = 1; - *alpha = 0x1; - break; - } } static cairo_status_t @@ -400,36 +248,33 @@ _cairo_glitz_surface_set_image (void *abstract_surface, cairo_glitz_surface_t *surface = abstract_surface; glitz_buffer_t *buffer; glitz_pixel_format_t pf; - uint32_t bpp, am, rm, gm, bm; + cairo_format_masks_t masks; char *data; - cairo_format_get_masks (image->format, &bpp, &am, &rm, &gm, &bm); + _pixman_format_to_masks (image->pixman_format, &masks); pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = bpp; - pf.masks.alpha_mask = am; - pf.masks.red_mask = rm; - pf.masks.green_mask = gm; - pf.masks.blue_mask = bm; + pf.masks.bpp = masks.bpp; + pf.masks.alpha_mask = masks.alpha_mask; + pf.masks.red_mask = masks.red_mask; + pf.masks.green_mask = masks.green_mask; + pf.masks.blue_mask = masks.blue_mask; pf.xoffset = src_x; pf.skip_lines = src_y; /* check for negative stride */ - if (image->stride < 0) - { + if (image->stride < 0) { pf.bytes_per_line = -image->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; data = (char *) image->data + image->stride * (image->height - 1); - } - else - { + } else { pf.bytes_per_line = image->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; data = (char *) image->data; } buffer = glitz_buffer_create_for_data (data); - if (!buffer) + if (buffer == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); glitz_set_pixels (surface->surface, @@ -455,6 +300,20 @@ _cairo_glitz_surface_acquire_source_image (void *abstract_surface, return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); } +static cairo_surface_t * +_cairo_glitz_surface_snapshot (void *abstract_surface) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_image_surface_t *image; + + status = _cairo_glitz_surface_get_image (surface, NULL, &image, NULL); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + return &image->base; +} + static void _cairo_glitz_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, @@ -507,6 +366,7 @@ _cairo_glitz_surface_release_dest_image (void *abstract_surfa static cairo_status_t _cairo_glitz_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -533,43 +393,27 @@ _cairo_glitz_surface_clone_similar (void *abstract_surface, else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - cairo_content_t content; - cairo_rectangle_int_t image_extent; - cairo_rectangle_int_t extent; - - content = _cairo_content_from_format (image_src->format); clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (surface, content, - image_src->width, - image_src->height); + _cairo_glitz_surface_create_similar (surface, src->content, + width, height); if (clone == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; if (clone->base.status) return clone->base.status; - image_extent.x = 0; - image_extent.y = 0; - image_extent.width = image_src->width; - image_extent.height = image_src->height; - extent.x = src_x; - extent.y = src_y; - extent.width = width; - extent.height = height; - - _cairo_rectangle_intersect(&extent, &image_extent); - status = _cairo_glitz_surface_set_image (clone, image_src, - extent.x, extent.y, - extent.width, extent.height, - extent.x, extent.y); + src_x, src_y, + width, height, + 0, 0); if (status) { cairo_surface_destroy (&clone->base); return status; } *clone_out = &clone->base; - + *clone_offset_x = src_x; + *clone_offset_y = src_y; return CAIRO_STATUS_SUCCESS; } @@ -582,17 +426,17 @@ _cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, { glitz_transform_t transform; - transform.matrix[0][0] = _cairo_fixed_from_double (matrix->xx); - transform.matrix[0][1] = _cairo_fixed_from_double (matrix->xy); - transform.matrix[0][2] = _cairo_fixed_from_double (matrix->x0); + transform.matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); + transform.matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); + transform.matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); - transform.matrix[1][0] = _cairo_fixed_from_double (matrix->yx); - transform.matrix[1][1] = _cairo_fixed_from_double (matrix->yy); - transform.matrix[1][2] = _cairo_fixed_from_double (matrix->y0); + transform.matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); + transform.matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); + transform.matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); transform.matrix[2][0] = 0; transform.matrix[2][1] = 0; - transform.matrix[2][2] = _cairo_fixed_from_double (1); + transform.matrix[2][2] = _cairo_fixed_16_16_from_double (1); glitz_surface_set_transform (surface->surface, &transform); } @@ -718,7 +562,7 @@ typedef struct _cairo_glitz_surface_attributes { } cairo_glitz_surface_attributes_t; static cairo_int_status_t -_cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, +_cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_glitz_surface_t *dst, int x, int y, @@ -836,22 +680,22 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, { cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; - params[0] = grad->p1.x; - params[1] = grad->p1.y; - params[2] = grad->p2.x; - params[3] = grad->p2.y; + params[0] = _cairo_fixed_to_16_16 (grad->p1.x); + params[1] = _cairo_fixed_to_16_16 (grad->p1.y); + params[2] = _cairo_fixed_to_16_16 (grad->p2.x); + params[3] = _cairo_fixed_to_16_16 (grad->p2.y); attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; } else { cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; - params[0] = grad->c1.x; - params[1] = grad->c1.y; - params[2] = grad->r1; - params[3] = grad->c2.x; - params[4] = grad->c2.y; - params[5] = grad->r2; + params[0] = _cairo_fixed_to_16_16 (grad->c1.x); + params[1] = _cairo_fixed_to_16_16 (grad->c1.y); + params[2] = _cairo_fixed_to_16_16 (grad->r1); + params[3] = _cairo_fixed_to_16_16 (grad->c2.x); + params[4] = _cairo_fixed_to_16_16 (grad->c2.y); + params[5] = _cairo_fixed_to_16_16 (grad->r2); attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; } @@ -887,7 +731,9 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, cairo_int_status_t status; status = _cairo_pattern_acquire_surface (pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, x, y, width, height, + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attr->base); if (status) @@ -937,7 +783,7 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, } static void -_cairo_glitz_pattern_release_surface (cairo_pattern_t *pattern, +_cairo_glitz_pattern_release_surface (const cairo_pattern_t *pattern, cairo_glitz_surface_t *surface, cairo_glitz_surface_attributes_t *attr) { @@ -948,8 +794,8 @@ _cairo_glitz_pattern_release_surface (cairo_pattern_t *pattern, } static cairo_int_status_t -_cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, - cairo_pattern_t *mask, +_cairo_glitz_pattern_acquire_surfaces (const cairo_pattern_t *src, + const cairo_pattern_t *mask, cairo_glitz_surface_t *dst, int src_x, int src_y, @@ -985,37 +831,31 @@ _cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, _cairo_pattern_init_solid (&tmp, &combined, CAIRO_CONTENT_COLOR_ALPHA); mask = NULL; - } else { - status = _cairo_pattern_init_copy (&tmp.base, src); - if (status) - return status; + src = &tmp.base; } - status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + status = _cairo_glitz_pattern_acquire_surface (src, dst, src_x, src_y, width, height, src_out, sattr); - _cairo_pattern_fini (&tmp.base); + if (src == &tmp.base) + _cairo_pattern_fini (&tmp.base); if (status) return status; if (mask) { - status = _cairo_pattern_init_copy (&tmp.base, mask); - if (status) - return status; - - status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + status = _cairo_glitz_pattern_acquire_surface (mask, dst, mask_x, mask_y, width, height, mask_out, mattr); - if (status) - _cairo_glitz_pattern_release_surface (&tmp.base, *src_out, sattr); - - _cairo_pattern_fini (&tmp.base); + if (status) { + /* XXX src == &tmp.base -> invalid (currently inconsequential) */ + _cairo_glitz_pattern_release_surface (src, *src_out, sattr); + } return status; } @@ -1039,8 +879,8 @@ _cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, static cairo_int_status_t _cairo_glitz_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -1087,11 +927,6 @@ _cairo_glitz_surface_composite (cairo_operator_t op, mask_y + mask_attr.base.y_offset, dst_x, dst_y, width, height); - - if (mask_attr.n_params) - free (mask_attr.params); - - _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); } else { @@ -1106,14 +941,50 @@ _cairo_glitz_surface_composite (cairo_operator_t op, width, height); } + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (status == CAIRO_STATUS_SUCCESS && + ! _cairo_operator_bounded_by_source (op)) + { + int src_width, src_height; + int mask_width, mask_height; + + src_width = glitz_surface_get_width (src->surface); + src_height = glitz_surface_get_height (src->surface); + if (mask) + { + mask_width = glitz_surface_get_width (mask->surface); + mask_height = glitz_surface_get_height (mask->surface); + } + else + { + mask_width = 0; + mask_height = 0; + } + status = _cairo_surface_composite_fixup_unbounded (&dst->base, + &src_attr.base, + src_width, src_height, + mask ? &mask_attr.base : NULL, + mask_width, mask_height, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, width, height); + } + + if (mask) + { + if (mask_attr.n_params) + free (mask_attr.params); + + _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); + } + if (src_attr.n_params) free (src_attr.params); _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr); - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - return CAIRO_STATUS_SUCCESS; } @@ -1126,24 +997,47 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, { cairo_glitz_surface_t *dst = abstract_dst; cairo_glitz_surface_t *src; + glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)]; + glitz_rectangle_t *glitz_rects = stack_rects; + glitz_rectangle_t *current_rect; + int i; + + if (n_rects > ARRAY_LENGTH (stack_rects)) { + glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t)); + if (glitz_rects == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < n_rects; i++) { + glitz_rects[i].x = rects[i].x; + glitz_rects[i].y = rects[i].y; + glitz_rects[i].width = rects[i].width; + glitz_rects[i].height = rects[i].height; + } switch (op) { + case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: { glitz_color_t glitz_color; + glitz_format_t *format; glitz_color.red = color->red_short; glitz_color.green = color->green_short; glitz_color.blue = color->blue_short; glitz_color.alpha = color->alpha_short; - glitz_set_rectangles (dst->surface, &glitz_color, - (glitz_rectangle_t *) rects, n_rects); - } break; - case CAIRO_OPERATOR_CLEAR: { - static const glitz_color_t glitz_color = { 0, 0, 0, 0 }; + /* + * XXX even if the dst surface don't have an alpha channel, the + * above alpha still effect the dst surface because the + * underlying glitz drawable may have an alpha channel. So + * replacing the color with an opaque one is needed. + */ + format = glitz_surface_get_format (dst->surface); + if (format->color.alpha_size == 0) + glitz_color.alpha = 0xffff; - glitz_set_rectangles (dst->surface, &glitz_color, - (glitz_rectangle_t *) rects, n_rects); + glitz_set_rectangles (dst->surface, &glitz_color, + glitz_rects, n_rects); } break; case CAIRO_OPERATOR_SATURATE: return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1160,7 +1054,11 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, case CAIRO_OPERATOR_ADD: default: if (_glitz_ensure_target (dst->surface)) + { + if (glitz_rects != stack_rects) + free (glitz_rects); return CAIRO_INT_STATUS_UNSUPPORTED; + } src = (cairo_glitz_surface_t *) _cairo_surface_create_similar_solid (&dst->base, @@ -1168,10 +1066,15 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, 1, 1, (cairo_color_t *) color); if (src->base.status) + { + if (glitz_rects != stack_rects) + free (glitz_rects); return src->base.status; + } glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); + current_rect = glitz_rects; while (n_rects--) { glitz_composite (_glitz_operator (op), @@ -1180,15 +1083,18 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, dst->surface, 0, 0, 0, 0, - rects->x, rects->y, - rects->width, rects->height); - rects++; + current_rect->x, current_rect->y, + current_rect->width, current_rect->height); + current_rect++; } cairo_surface_destroy (&src->base); break; } + if (glitz_rects != stack_rects) + free (glitz_rects); + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1197,7 +1103,7 @@ _cairo_glitz_surface_fill_rectangles (void *abstract_dst, static cairo_int_status_t _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, @@ -1209,8 +1115,6 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, cairo_trapezoid_t *traps, int n_traps) { - cairo_pattern_union_t tmp_src_pattern; - cairo_pattern_t *src_pattern; cairo_glitz_surface_attributes_t attributes; cairo_glitz_surface_t *dst = abstract_dst; cairo_glitz_surface_t *src; @@ -1219,13 +1123,15 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, void *data = NULL; cairo_int_status_t status; unsigned short alpha; + pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; + pixman_trapezoid_t *pixman_traps = stack_traps; + int i; if (antialias != CAIRO_ANTIALIAS_DEFAULT && antialias != CAIRO_ANTIALIAS_GRAY) + { return CAIRO_INT_STATUS_UNSUPPORTED; - - if (dst->base.status) - return dst->base.status; + } if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1233,34 +1139,36 @@ _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_TYPE_SURFACE) - { - status = _cairo_pattern_init_copy (&tmp_src_pattern.base, pattern); - if (status) - return status; - - status = _cairo_glitz_pattern_acquire_surface (&tmp_src_pattern.base, - dst, - src_x, src_y, - width, height, - &src, &attributes); - src_pattern = &tmp_src_pattern.base; + /* Convert traps to pixman traps */ + if (n_traps > ARRAY_LENGTH (stack_traps)) { + pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t)); + if (pixman_traps == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - else - { - status = _cairo_glitz_pattern_acquire_surface (pattern, dst, - src_x, src_y, - width, height, - &src, &attributes); - src_pattern = pattern; + + for (i = 0; i < n_traps; i++) { + pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); + pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); + pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); + pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); + pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); + pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); + pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); + pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); + pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); + pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); } - alpha = 0xffff; + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, + src_x, src_y, + width, height, + &src, &attributes); if (status) - return status; + goto FAIL; - if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) - { + alpha = 0xffff; + + if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) { static const glitz_color_t clear_black = { 0, 0, 0, 0 }; glitz_color_t color; glitz_geometry_format_t format; @@ -1282,17 +1190,12 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, CAIRO_CONTENT_ALPHA, 2, 1); if (mask == NULL) { - _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); - if (src_pattern == &tmp_src_pattern.base) - _cairo_pattern_fini (&tmp_src_pattern.base); - return CAIRO_INT_STATUS_UNSUPPORTED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; } if (mask->base.status) { - _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); - if (src_pattern == &tmp_src_pattern.base) - _cairo_pattern_fini (&tmp_src_pattern.base); - - return mask->base.status; + status = mask->base.status; + goto FAIL; } color.red = color.green = color.blue = color.alpha = 0xffff; @@ -1307,20 +1210,15 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, size *= format.vertex.bytes_per_vertex; - while (n_traps) - { - if (data_size < size) - { + while (n_traps) { + if (data_size < size) { void *p; + data_size = size; p = realloc (data, data_size); - if (!p) - { - _cairo_glitz_pattern_release_surface (src_pattern, src, - &attributes); - if (src_pattern == &tmp_src_pattern.base) - _cairo_pattern_fini (&tmp_src_pattern.base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (p == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; } data = p; @@ -1328,13 +1226,10 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, glitz_buffer_destroy (buffer); buffer = glitz_buffer_create_for_data (data); - if (!buffer) { + if (buffer == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); free (data); - _cairo_glitz_pattern_release_surface (src_pattern, src, - &attributes); - if (src_pattern == &tmp_src_pattern.base) - _cairo_pattern_fini (&tmp_src_pattern.base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; } } @@ -1342,7 +1237,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, glitz_add_trapezoids (buffer, offset, size - offset, format.vertex.type, mask->surface, - (glitz_trapezoid_t *) traps, n_traps, + (glitz_trapezoid_t *) pixman_traps, n_traps, &n_trap_added); n_traps -= n_trap_added; @@ -1356,21 +1251,16 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, glitz_set_array (dst->surface, 0, 3, offset / format.vertex.bytes_per_vertex, 0, 0); - } - else - { + } else { cairo_image_surface_t *image; unsigned char *ptr; int stride; stride = (width + 3) & -4; data = calloc (stride, height); - if (!data) - { - _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); - if (src_pattern == &tmp_src_pattern.base) - _cairo_pattern_fini (&tmp_src_pattern.base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (data == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; } /* using negative stride */ @@ -1381,34 +1271,33 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, CAIRO_FORMAT_A8, width, height, -stride); - if (image->base.status) - { - cairo_surface_destroy (&src->base); + status = image->base.status; + if (status) { free (data); - return image->base.status; + goto FAIL; } pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, - n_traps, (pixman_trapezoid_t *) traps); + n_traps, (pixman_trapezoid_t *) pixman_traps); mask = (cairo_glitz_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, CAIRO_CONTENT_ALPHA, width, height); - if (mask->base.status) { - _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); + status = mask->base.status; + if (status) { free (data); cairo_surface_destroy (&image->base); - return mask->base.status; + goto FAIL; } status = _cairo_glitz_surface_set_image (mask, image, 0, 0, width, height, 0, 0); - cairo_surface_destroy(&image->base); + cairo_surface_destroy (&image->base); if (status) - return status; + goto FAIL; } _cairo_glitz_surface_set_attributes (src, &attributes); @@ -1435,17 +1324,33 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, free (data); - _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes); - if (src_pattern == &tmp_src_pattern.base) - _cairo_pattern_fini (&tmp_src_pattern.base); + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } - if (mask) + if (! _cairo_operator_bounded_by_mask (op)) { + status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, + &attributes.base, + glitz_surface_get_width (src->surface), + glitz_surface_get_height (src->surface), + width, height, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height); + } + +FAIL: + _cairo_glitz_pattern_release_surface (pattern, src, &attributes); + + if (mask != NULL) cairo_surface_destroy (&mask->base); - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (pixman_traps != stack_traps) + free (pixman_traps); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -1454,43 +1359,23 @@ _cairo_glitz_surface_set_clip_region (void *abstract_surface, { cairo_glitz_surface_t *surface = abstract_surface; - if (region) - { - glitz_box_t *box; - int n; + if (region != NULL) { cairo_status_t status; - if (!surface->has_clip) { - _cairo_region_init (&surface->clip); - surface->has_clip = TRUE; - } - - status = _cairo_region_copy (&surface->clip, region); - if (status) { - _cairo_region_fini (&surface->clip); - surface->has_clip = FALSE; - return status; - } - - status = _cairo_glitz_get_boxes_from_region (&surface->clip, &box, &n); - if (status) { - _cairo_region_fini (&surface->clip); - surface->has_clip = FALSE; + status = _cairo_glitz_get_boxes_from_region (region, + &surface->clip_boxes, + &surface->num_clip_boxes); + if (status) return status; - } - glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); - - free (box); - } - else - { + glitz_surface_set_clip_region (surface->surface, + 0, 0, + surface->clip_boxes, + surface->num_clip_boxes); + surface->has_clip = TRUE; + } else { glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - - if (surface->has_clip) { - _cairo_region_fini (&surface->clip); - surface->has_clip = FALSE; - } + surface->has_clip = FALSE; } return CAIRO_STATUS_SUCCESS; @@ -1504,7 +1389,7 @@ _cairo_glitz_surface_get_extents (void *abstract_surface, rectangle->x = 0; rectangle->y = 0; - rectangle->width = glitz_surface_get_width (surface->surface); + rectangle->width = glitz_surface_get_width (surface->surface); rectangle->height = glitz_surface_get_height (surface->surface); return CAIRO_STATUS_SUCCESS; @@ -2002,7 +1887,7 @@ _cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, glitz_point_fixed_t p1, p2; glitz_pixel_format_t pf; glitz_buffer_t *buffer; - unsigned int bpp, am, rm, gm, bm; + cairo_format_masks_t masks; cairo_int_status_t status; glyph_private = scaled_glyph->surface_private; @@ -2057,14 +1942,14 @@ _cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - cairo_format_get_masks (glyph_surface->format, &bpp, &am, &rm, &gm, &bm); + _pixman_format_to_masks (glyph_surface->pixman_format, &masks); pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = bpp; - pf.masks.alpha_mask = am; - pf.masks.red_mask = rm; - pf.masks.green_mask = gm; - pf.masks.blue_mask = bm; + pf.masks.bpp = masks.bpp; + pf.masks.alpha_mask = masks.alpha_mask; + pf.masks.red_mask = masks.red_mask; + pf.masks.green_mask = masks.green_mask; + pf.masks.blue_mask = masks.blue_mask; pf.bytes_per_line = glyph_surface->stride; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; @@ -2101,7 +1986,7 @@ _cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, static cairo_int_status_t _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *abstract_surface, int src_x, int src_y, @@ -2146,6 +2031,10 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; + /* XXX Unbounded operators are not handled correctly */ + if (! _cairo_operator_bounded_by_mask (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2261,6 +2150,7 @@ _cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, status = _cairo_glitz_surface_clone_similar (abstract_surface, image, + CAIRO_CONTENT_COLOR_ALPHA, 0, 0, glyph_width, @@ -2421,6 +2311,8 @@ static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_composite, _cairo_glitz_surface_fill_rectangles, _cairo_glitz_surface_composite_trapezoids, + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_glitz_surface_set_clip_region, @@ -2439,7 +2331,7 @@ static const cairo_surface_backend_t cairo_glitz_surface_backend = { NULL, /* fill */ NULL, /* show_glyphs */ - NULL, /* snapshot */ + _cairo_glitz_surface_snapshot, _cairo_glitz_surface_is_similar, _cairo_glitz_surface_reset @@ -2482,14 +2374,17 @@ cairo_glitz_surface_create (glitz_surface_t *surface) format = glitz_surface_get_format (surface); _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend, - _glitz_format_to_content(format)); + _glitz_format_to_content (format)); glitz_surface_reference (surface); crsurface->surface = surface; crsurface->format = format; - crsurface->has_clip = FALSE; - return (cairo_surface_t *) crsurface; + crsurface->has_clip = FALSE; + crsurface->clip_boxes = NULL; + crsurface->num_clip_boxes = 0; + + return &crsurface->base; } slim_hidden_def (cairo_glitz_surface_create); diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h index 2590048..cb278e8 100644 --- a/src/cairo-gstate-private.h +++ b/src/cairo-gstate-private.h @@ -50,6 +50,7 @@ struct _cairo_gstate { cairo_font_face_t *font_face; cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */ + cairo_scaled_font_t *previous_scaled_font; /* holdover */ cairo_matrix_t font_matrix; cairo_font_options_t font_options; diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 28a9538..8f6f01b 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -77,6 +77,8 @@ _cairo_gstate_init (cairo_gstate_t *gstate, { cairo_status_t status; + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); + gstate->next = NULL; gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT; @@ -90,6 +92,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, gstate->font_face = NULL; gstate->scaled_font = NULL; + gstate->previous_scaled_font = NULL; cairo_matrix_init_scale (&gstate->font_matrix, CAIRO_GSTATE_DEFAULT_FONT_SIZE, @@ -107,8 +110,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, gstate->ctm_inverse = gstate->ctm; gstate->source_ctm_inverse = gstate->ctm; - gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK, - CAIRO_CONTENT_COLOR); + gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base; /* Now that the gstate is fully initialized and ready for the eventual * _cairo_gstate_fini(), we can check for errors (and not worry about @@ -118,11 +120,11 @@ _cairo_gstate_init (cairo_gstate_t *gstate, return _cairo_error (CAIRO_STATUS_NULL_POINTER); status = target->status; - if (status) + if (unlikely (status)) return status; status = gstate->source->status; - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -140,6 +142,8 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) { cairo_status_t status; + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); + gstate->op = other->op; gstate->tolerance = other->tolerance; @@ -147,23 +151,25 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) status = _cairo_stroke_style_init_copy (&gstate->stroke_style, &other->stroke_style); - if (status) + if (unlikely (status)) return status; gstate->fill_rule = other->fill_rule; gstate->font_face = cairo_font_face_reference (other->font_face); gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font); + gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font); gstate->font_matrix = other->font_matrix; _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); status = _cairo_clip_init_copy (&gstate->clip, &other->clip); - if (status) { + if (unlikely (status)) { _cairo_stroke_style_fini (&gstate->stroke_style); cairo_font_face_destroy (gstate->font_face); cairo_scaled_font_destroy (gstate->scaled_font); + cairo_scaled_font_destroy (gstate->previous_scaled_font); return status; } @@ -191,6 +197,9 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_font_face_destroy (gstate->font_face); gstate->font_face = NULL; + cairo_scaled_font_destroy (gstate->previous_scaled_font); + gstate->previous_scaled_font = NULL; + cairo_scaled_font_destroy (gstate->scaled_font); gstate->scaled_font = NULL; @@ -207,6 +216,8 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_pattern_destroy (gstate->source); gstate->source = NULL; + + VG (VALGRIND_MAKE_MEM_NOACCESS (gstate, sizeof (cairo_gstate_t))); } /** @@ -223,16 +234,19 @@ _cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) cairo_gstate_t *top; cairo_status_t status; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + top = *freelist; if (top == NULL) { top = malloc (sizeof (cairo_gstate_t)); - if (top == NULL) + if (unlikely (top == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else *freelist = top->next; status = _cairo_gstate_init_copy (top, *gstate); - if (status) { + if (unlikely (status)) { top->next = *freelist; *freelist = top; return status; @@ -262,6 +276,7 @@ _cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) *gstate = top->next; _cairo_gstate_fini (top); + VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *))); top->next = *freelist; *freelist = top; @@ -302,7 +317,7 @@ _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) _cairo_clip_reset (&gstate->clip); status = _cairo_clip_init_deep_copy (&gstate->clip, &gstate->next->clip, child); - if (status) + if (unlikely (status)) return status; /* The clip is in surface backend coordinates for the previous target; @@ -406,6 +421,12 @@ _cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t * _cairo_gstate_get_source (cairo_gstate_t *gstate) { + if (gstate->source == &_cairo_pattern_black.base) { + /* do not expose the static object to the user */ + gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK, + CAIRO_CONTENT_COLOR); + } + return gstate->source; } @@ -511,7 +532,7 @@ _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dash } gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double)); - if (gstate->stroke_style.dash == NULL) { + if (unlikely (gstate->stroke_style.dash == NULL)) { gstate->stroke_style.num_dashes = 0; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -667,7 +688,7 @@ _cairo_gstate_transform (cairo_gstate_t *gstate, tmp = *matrix; status = cairo_matrix_invert (&tmp); - if (status) + if (unlikely (status)) return status; _cairo_gstate_unset_scaled_font (gstate); @@ -782,20 +803,16 @@ _cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) } */ -cairo_status_t +void _cairo_gstate_path_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { - cairo_status_t status; double px1, py1, px2, py2; - status = _cairo_path_fixed_bounds (path, - &px1, &py1, &px2, &py2, - gstate->tolerance); - if (status) - return status; + _cairo_path_fixed_bounds (path, + &px1, &py1, &px2, &py2); _cairo_gstate_backend_to_user_rectangle (gstate, &px1, &py1, &px2, &py2, @@ -808,40 +825,57 @@ _cairo_gstate_path_extents (cairo_gstate_t *gstate, *x2 = px2; if (y2) *y2 = py2; - - return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, - cairo_pattern_t *pattern, + cairo_pattern_t **pattern, cairo_pattern_t *original, cairo_matrix_t *ctm_inverse) { - cairo_surface_pattern_t *surface_pattern; - cairo_surface_t *surface; cairo_status_t status; - - status = _cairo_pattern_init_copy (pattern, original); - if (status) - return status; + cairo_bool_t have_copy = FALSE; /* apply device_transform first so that it is transformed by ctm_inverse */ - if (cairo_pattern_get_type (original) == CAIRO_PATTERN_TYPE_SURFACE) { + if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *surface; + surface_pattern = (cairo_surface_pattern_t *) original; surface = surface_pattern->surface; - if (_cairo_surface_has_device_transform (surface)) - _cairo_pattern_transform (pattern, &surface->device_transform); + + if (_cairo_surface_has_device_transform (surface)) { + status = _cairo_pattern_init_copy (*pattern, original); + if (unlikely (status)) + return status; + + have_copy = TRUE; + + _cairo_pattern_transform (*pattern, &surface->device_transform); + } } - _cairo_pattern_transform (pattern, ctm_inverse); + if (! _cairo_matrix_is_identity (ctm_inverse)) { + if (! have_copy) { + status = _cairo_pattern_init_copy (*pattern, original); + if (unlikely (status)) + return status; + + have_copy = TRUE; + } + + _cairo_pattern_transform (*pattern, ctm_inverse); + } + + if (! have_copy) + *pattern = original; return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, - cairo_pattern_t *pattern) +_cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, + cairo_pattern_t **pattern) { return _cairo_gstate_copy_transformed_pattern (gstate, pattern, gstate->source, @@ -849,9 +883,9 @@ _cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, } static cairo_status_t -_cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, - cairo_pattern_t *pattern, - cairo_pattern_t *mask) +_cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, + cairo_pattern_t **pattern, + cairo_pattern_t *mask) { return _cairo_gstate_copy_transformed_pattern (gstate, pattern, mask, @@ -862,24 +896,27 @@ cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate) { cairo_status_t status; - cairo_pattern_union_t pattern; + cairo_pattern_t *pattern; + cairo_pattern_union_t pattern_stack; if (gstate->source->status) return gstate->source->status; status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (status) + if (unlikely (status)) return status; - status = _cairo_gstate_copy_transformed_source (gstate, &pattern.base); - if (status) + pattern = &pattern_stack.base; + status = _cairo_gstate_copy_transformed_source (gstate, &pattern); + if (unlikely (status)) return status; status = _cairo_surface_paint (gstate->target, gstate->op, - &pattern.base); + pattern, NULL); - _cairo_pattern_fini (&pattern.base); + if (pattern == &pattern_stack.base) + _cairo_pattern_fini (pattern); return status; } @@ -889,7 +926,8 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_pattern_t *mask) { cairo_status_t status; - cairo_pattern_union_t source_pattern, mask_pattern; + cairo_pattern_union_t source_pattern_stack, mask_pattern_stack; + cairo_pattern_t *source_pattern, *mask_pattern; if (mask->status) return mask->status; @@ -898,25 +936,29 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, return gstate->source->status; status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (status) + if (unlikely (status)) return status; - status = _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - if (status) + source_pattern = &source_pattern_stack.base; + status = _cairo_gstate_copy_transformed_source (gstate, &source_pattern); + if (unlikely (status)) return status; - status = _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); - if (status) + mask_pattern = &mask_pattern_stack.base; + status = _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern, mask); + if (unlikely (status)) goto CLEANUP_SOURCE; status = _cairo_surface_mask (gstate->target, gstate->op, - &source_pattern.base, - &mask_pattern.base); + source_pattern, + mask_pattern, NULL); - _cairo_pattern_fini (&mask_pattern.base); + if (mask_pattern == &mask_pattern_stack.base) + _cairo_pattern_fini (&mask_pattern_stack.base); CLEANUP_SOURCE: - _cairo_pattern_fini (&source_pattern.base); + if (source_pattern == &source_pattern_stack.base) + _cairo_pattern_fini (&source_pattern_stack.base); return status; } @@ -925,7 +967,8 @@ cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_status_t status; - cairo_pattern_union_t source_pattern; + cairo_pattern_union_t source_pattern_stack; + cairo_pattern_t *source_pattern; if (gstate->source->status) return gstate->source->status; @@ -934,28 +977,29 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (status) + if (unlikely (status)) return status; + source_pattern = &source_pattern_stack.base; status = _cairo_gstate_copy_transformed_source (gstate, - &source_pattern.base); - if (status) + &source_pattern); + if (unlikely (status)) return status; status = _cairo_surface_stroke (gstate->target, gstate->op, - &source_pattern.base, + source_pattern, path, &gstate->stroke_style, &gstate->ctm, &gstate->ctm_inverse, gstate->tolerance, - gstate->antialias); + gstate->antialias, NULL); - _cairo_pattern_fini (&source_pattern.base); + if (source_pattern == &source_pattern_stack.base) + _cairo_pattern_fini (&source_pattern_stack.base); return status; - } cairo_status_t @@ -966,6 +1010,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, cairo_bool_t *inside_ret) { cairo_status_t status; + cairo_rectangle_int_t extents; cairo_box_t limit; cairo_traps_t traps; @@ -976,6 +1021,20 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, _cairo_gstate_user_to_backend (gstate, &x, &y); + /* Before we perform the expensive stroke analysis, + * check whether the point is within the extents of the path. + */ + _cairo_path_fixed_approximate_stroke_extents (path, + &gstate->stroke_style, + &gstate->ctm, + &extents); + if (x < extents.x || x > extents.x + extents.width || + y < extents.y || y > extents.y + extents.height) + { + *inside_ret = FALSE; + return CAIRO_STATUS_SUCCESS; + } + limit.p1.x = _cairo_fixed_from_double (x) - 1; limit.p1.y = _cairo_fixed_from_double (y) - 1; limit.p2.x = limit.p1.x + 2; @@ -990,7 +1049,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, &gstate->ctm_inverse, gstate->tolerance, &traps); - if (status) + if (unlikely (status)) goto BAIL; *inside_ret = _cairo_traps_contain (&traps, x, y); @@ -1005,66 +1064,50 @@ cairo_status_t _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_status_t status; - cairo_pattern_union_t pattern; + cairo_pattern_union_t pattern_stack; + cairo_pattern_t *pattern; if (gstate->source->status) return gstate->source->status; status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (status) + if (unlikely (status)) return status; - status = _cairo_gstate_copy_transformed_source (gstate, &pattern.base); - if (status) + pattern = &pattern_stack.base; + status = _cairo_gstate_copy_transformed_source (gstate, &pattern); + if (unlikely (status)) return status; status = _cairo_surface_fill (gstate->target, gstate->op, - &pattern.base, + pattern, path, gstate->fill_rule, gstate->tolerance, - gstate->antialias); + gstate->antialias, + NULL); - _cairo_pattern_fini (&pattern.base); + if (pattern == &pattern_stack.base) + _cairo_pattern_fini (&pattern_stack.base); return status; } -cairo_status_t +void _cairo_gstate_in_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, double y, cairo_bool_t *inside_ret) { - cairo_status_t status; - cairo_box_t limit; - cairo_traps_t traps; - _cairo_gstate_user_to_backend (gstate, &x, &y); - limit.p1.x = _cairo_fixed_from_double (x) - 1; - limit.p1.y = _cairo_fixed_from_double (y) - 1; - limit.p2.x = limit.p1.x + 2; - limit.p2.y = limit.p1.y + 2; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, &limit); - - status = _cairo_path_fixed_fill_to_traps (path, - gstate->fill_rule, - gstate->tolerance, - &traps); - if (status) - goto BAIL; - - *inside_ret = _cairo_traps_contain (&traps, x, y); - -BAIL: - _cairo_traps_fini (&traps); - - return status; + _cairo_path_fixed_in_fill (path, + gstate->fill_rule, + gstate->tolerance, + x, y, + inside_ret); } cairo_status_t @@ -1210,7 +1253,7 @@ _cairo_gstate_int_clip_extents (cairo_gstate_t *gstate, cairo_status_t status; status = _cairo_surface_get_extents (gstate->target, extents); - if (status) + if (unlikely (status)) return status; status = _cairo_clip_intersect_to_rectangle (&gstate->clip, extents); @@ -1230,7 +1273,7 @@ _cairo_gstate_clip_extents (cairo_gstate_t *gstate, cairo_status_t status; status = _cairo_gstate_int_clip_extents (gstate, &extents); - if (status) + if (unlikely (status)) return status; px1 = extents.x; @@ -1263,10 +1306,14 @@ _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) static void _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) { - if (gstate->scaled_font) { - cairo_scaled_font_destroy (gstate->scaled_font); - gstate->scaled_font = NULL; - } + if (gstate->scaled_font == NULL) + return; + + if (gstate->previous_scaled_font != NULL) + cairo_scaled_font_destroy (gstate->previous_scaled_font); + + gstate->previous_scaled_font = gstate->scaled_font; + gstate->scaled_font = NULL; } cairo_status_t @@ -1349,7 +1396,7 @@ _cairo_gstate_get_font_face (cairo_gstate_t *gstate, cairo_status_t status; status = _cairo_gstate_ensure_font_face (gstate); - if (status) + if (unlikely (status)) return status; *font_face = gstate->font_face; @@ -1364,7 +1411,7 @@ _cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); - if (status) + if (unlikely (status)) return status; *scaled_font = gstate->scaled_font; @@ -1480,7 +1527,7 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) return gstate->scaled_font->status; status = _cairo_gstate_ensure_font_face (gstate); - if (status) + if (unlikely (status)) return status; cairo_surface_get_font_options (gstate->target, &options); @@ -1492,7 +1539,7 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) &options); status = cairo_scaled_font_status (scaled_font); - if (status) + if (unlikely (status)) return status; gstate->scaled_font = scaled_font; @@ -1505,7 +1552,7 @@ _cairo_gstate_get_font_extents (cairo_gstate_t *gstate, cairo_font_extents_t *extents) { cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate); - if (status) + if (unlikely (status)) return status; cairo_scaled_font_extents (gstate->scaled_font, extents); @@ -1528,7 +1575,7 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); - if (status) + if (unlikely (status)) return status; return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y, @@ -1543,7 +1590,7 @@ _cairo_gstate_set_font_face (cairo_gstate_t *gstate, cairo_font_face_t *font_face) { if (font_face && font_face->status) - return font_face->status; + return _cairo_error (font_face->status); if (font_face == gstate->font_face) return CAIRO_STATUS_SUCCESS; @@ -1565,7 +1612,7 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); - if (status) + if (unlikely (status)) return status; cairo_scaled_font_glyph_extents (gstate->scaled_font, @@ -1585,7 +1632,8 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, int num_clusters, cairo_text_cluster_flags_t cluster_flags) { - cairo_pattern_union_t source_pattern; + cairo_pattern_union_t source_pattern_stack; + cairo_pattern_t *source_pattern; cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_glyph_t *transformed_glyphs; cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; @@ -1596,11 +1644,11 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, return gstate->source->status; status = _cairo_surface_set_clip (gstate->target, &gstate->clip); - if (status) + if (unlikely (status)) return status; status = _cairo_gstate_ensure_scaled_font (gstate); - if (status) + if (unlikely (status)) return status; transformed_glyphs = stack_transformed_glyphs; @@ -1608,7 +1656,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) { transformed_glyphs = cairo_glyph_allocate (num_glyphs); - if (transformed_glyphs == NULL) { + if (unlikely (transformed_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_GLYPHS; } @@ -1620,7 +1668,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { transformed_clusters = cairo_text_cluster_allocate (num_clusters); - if (transformed_clusters == NULL) { + if (unlikely (transformed_clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_GLYPHS; } @@ -1638,8 +1686,9 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, if (status || num_glyphs == 0) goto CLEANUP_GLYPHS; - status = _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - if (status) + source_pattern = &source_pattern_stack.base; + status = _cairo_gstate_copy_transformed_source (gstate, &source_pattern); + if (unlikely (status)) goto CLEANUP_GLYPHS; /* For really huge font sizes, we can just do path;fill instead of @@ -1656,12 +1705,12 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240) { status = _cairo_surface_show_text_glyphs (gstate->target, gstate->op, - &source_pattern.base, + source_pattern, utf8, utf8_len, transformed_glyphs, num_glyphs, transformed_clusters, num_clusters, cluster_flags, - gstate->scaled_font); + gstate->scaled_font, NULL); } else { cairo_path_fixed_t path; @@ -1674,16 +1723,17 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, if (status == CAIRO_STATUS_SUCCESS) status = _cairo_surface_fill (gstate->target, gstate->op, - &source_pattern.base, + source_pattern, &path, CAIRO_FILL_RULE_WINDING, gstate->tolerance, - gstate->scaled_font->options.antialias); + gstate->scaled_font->options.antialias, NULL); _cairo_path_fixed_fini (&path); } - _cairo_pattern_fini (&source_pattern.base); + if (source_pattern == &source_pattern_stack.base) + _cairo_pattern_fini (&source_pattern_stack.base); CLEANUP_GLYPHS: if (transformed_glyphs != stack_transformed_glyphs) @@ -1705,22 +1755,23 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; status = _cairo_gstate_ensure_scaled_font (gstate); - if (status) + if (unlikely (status)) return status; - if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) + if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) { transformed_glyphs = stack_transformed_glyphs; - else - transformed_glyphs = cairo_glyph_allocate (num_glyphs); - if (transformed_glyphs == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + transformed_glyphs = cairo_glyph_allocate (num_glyphs); + if (unlikely (transformed_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } status = _cairo_gstate_transform_glyphs_to_backend (gstate, glyphs, num_glyphs, NULL, 0, 0, transformed_glyphs, NULL, NULL); - if (status) + if (unlikely (status)) goto CLEANUP_GLYPHS; status = _cairo_scaled_font_glyph_path (gstate->scaled_font, @@ -1788,7 +1839,6 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, if (num_transformed_glyphs != NULL) { cairo_rectangle_int_t surface_extents; - double scale = _cairo_scaled_font_get_max_scale (gstate->scaled_font); drop = TRUE; status = _cairo_gstate_int_clip_extents (gstate, &surface_extents); @@ -1798,6 +1848,7 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, if (status == CAIRO_INT_STATUS_UNSUPPORTED) { drop = FALSE; /* unbounded surface */ } else { + double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); if (surface_extents.width == 0 || surface_extents.height == 0) { /* No visible area. Don't draw anything */ *num_transformed_glyphs = 0; @@ -1811,10 +1862,10 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, * to device if it's going to be visible, but I'm not inclined to * do that now. */ - x1 = surface_extents.x - 2*scale; - y1 = surface_extents.y - 2*scale; - x2 = surface_extents.x + (int) surface_extents.width + scale; - y2 = surface_extents.y + (int) surface_extents.height + scale; + x1 = surface_extents.x - scale10; + y1 = surface_extents.y - scale10; + x2 = surface_extents.x + (int) surface_extents.width + scale10; + y2 = surface_extents.y + (int) surface_extents.height + scale10; } if (!drop) diff --git a/src/cairo-hash-private.h b/src/cairo-hash-private.h index 9101f2e..32078bd 100644 --- a/src/cairo-hash-private.h +++ b/src/cairo-hash-private.h @@ -51,7 +51,7 @@ typedef cairo_bool_t (*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); +(*cairo_hash_predicate_func_t) (const void *entry); typedef void (*cairo_hash_callback_func_t) (void *entry, @@ -63,10 +63,9 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal); cairo_private void _cairo_hash_table_destroy (cairo_hash_table_t *hash_table); -cairo_private cairo_bool_t +cairo_private void * _cairo_hash_table_lookup (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key, - cairo_hash_entry_t **entry_return); + cairo_hash_entry_t *key); cairo_private void * _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, @@ -81,7 +80,7 @@ _cairo_hash_table_remove (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key); cairo_private void -_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, +_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, cairo_hash_callback_func_t hash_callback, void *closure); diff --git a/src/cairo-hash.c b/src/cairo-hash.c index 2317eb1..51303f5 100644 --- a/src/cairo-hash.c +++ b/src/cairo-hash.c @@ -52,12 +52,11 @@ * Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer. */ -static cairo_hash_entry_t dead_entry = { 0 }; -#define DEAD_ENTRY (&dead_entry) +#define DEAD_ENTRY ((cairo_hash_entry_t *) 0x1) #define ENTRY_IS_FREE(entry) ((entry) == NULL) #define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) -#define ENTRY_IS_LIVE(entry) ((entry) && ! ENTRY_IS_DEAD(entry)) +#define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY) /* We expect keys will not be destroyed frequently, so our table does not * contain any explicit shrinking code nor any chain-coalescing code for @@ -109,7 +108,7 @@ static const cairo_hash_table_arrangement_t hash_table_arrangements [] = { { 4194304, 9227641, 9227639 }, { 8388608, 18455029, 18455027 }, { 16777216, 36911011, 36911009 }, - { 33554432, 73819861, 73819859 }, + { 33554432, 73819861, 73819859 }, { 67108864, 147639589, 147639587 }, { 134217728, 295279081, 295279079 }, { 268435456, 590559793, 590559791 } @@ -149,7 +148,7 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) cairo_hash_table_t *hash_table; hash_table = malloc (sizeof (cairo_hash_table_t)); - if (hash_table == NULL) { + if (unlikely (hash_table == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } @@ -160,7 +159,7 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) hash_table->entries = calloc (hash_table->arrangement->size, sizeof(cairo_hash_entry_t *)); - if (hash_table->entries == NULL) { + if (unlikely (hash_table->entries == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); free (hash_table); return NULL; @@ -206,85 +205,36 @@ _cairo_hash_table_destroy (cairo_hash_table_t *hash_table) free (hash_table); } -/** - * _cairo_hash_table_lookup_internal: - * - * @hash_table: a #cairo_hash_table_t to search - * @key: the key to search on - * @hash_code: the hash_code for @key - * @key_unique: If %TRUE, then caller asserts that no key already - * exists that will compare equal to #key, so search can be - * optimized. If unsure, set to %FALSE and the code will always work. - * - * Search the hashtable for a live entry for which - * hash_table->keys_equal returns true. If no such entry exists then - * return the first available (free or dead entry). - * - * If the key_unique flag is set, then the search will never call - * hash_table->keys_equal and will act as if it always returned - * false. This is useful as a performance optimization in special - * circumstances where the caller knows that there is no existing - * entry in the hash table with a matching key. - * - * Return value: The matching entry in the hash table (if - * any). Otherwise, the first available entry. The caller should check - * entry->state to check whether a match was found or not. - **/ static cairo_hash_entry_t ** -_cairo_hash_table_lookup_internal (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key, - cairo_bool_t key_is_unique) +_cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) { - cairo_hash_entry_t **entry, **first_available = NULL; unsigned long table_size, i, idx, step; + cairo_hash_entry_t **entry; table_size = hash_table->arrangement->size; - idx = key->hash % table_size; - step = 0; - - for (i = 0; i < table_size; ++i) - { - entry = &hash_table->entries[idx]; - - if (ENTRY_IS_FREE(*entry)) - { - return entry; - } - else if (ENTRY_IS_DEAD(*entry)) - { - if (key_is_unique) { - return entry; - } else { - if (! first_available) - first_available = entry; - } - } - else /* ENTRY_IS_LIVE(*entry) */ - { - if (! key_is_unique) - if (hash_table->keys_equal (key, *entry)) - return entry; - } - if (step == 0) { - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; - } + entry = &hash_table->entries[idx]; + if (! ENTRY_IS_LIVE (*entry)) + return entry; + i = 1; + step = key->hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { idx += step; if (idx >= table_size) idx -= table_size; - } - /* - * The table should not have permitted you to get here if you were just - * looking for a free slot: there should have been room. - */ - assert (key_is_unique == 0); + entry = &hash_table->entries[idx]; + if (! ENTRY_IS_LIVE (*entry)) + return entry; + } while (++i < table_size); - return first_available; + ASSERT_NOT_REACHED; + return NULL; } /** @@ -299,10 +249,9 @@ _cairo_hash_table_lookup_internal (cairo_hash_table_t *hash_table, * %CAIRO_STATUS_NO_MEMORY if out of memory. **/ static cairo_status_t -_cairo_hash_table_resize (cairo_hash_table_t *hash_table) +_cairo_hash_table_resize (cairo_hash_table_t *hash_table) { cairo_hash_table_t tmp; - cairo_hash_entry_t **entry; unsigned long new_size, i; /* This keeps the hash table between 25% and 50% full. */ @@ -331,16 +280,13 @@ _cairo_hash_table_resize (cairo_hash_table_t *hash_table) new_size = tmp.arrangement->size; tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*)); - if (tmp.entries == NULL) + if (unlikely (tmp.entries == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < hash_table->arrangement->size; ++i) { if (ENTRY_IS_LIVE (hash_table->entries[i])) { - entry = _cairo_hash_table_lookup_internal (&tmp, - hash_table->entries[i], - TRUE); - assert (ENTRY_IS_FREE(*entry)); - *entry = hash_table->entries[i]; + *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i]) + = hash_table->entries[i]; } } @@ -355,42 +301,57 @@ _cairo_hash_table_resize (cairo_hash_table_t *hash_table) * _cairo_hash_table_lookup: * @hash_table: a hash table * @key: the key of interest - * @entry_return: pointer for return value. * * Performs a lookup in @hash_table looking for an entry which has a * key that matches @key, (as determined by the keys_equal() function * passed to _cairo_hash_table_create). * - * Return value: %TRUE if there is an entry in the hash table that - * matches the given key, (which will now be in *entry_return). %FALSE - * otherwise, (in which case *entry_return will be %NULL). + * Return value: the matching entry, of %NULL if no match was found. **/ -cairo_bool_t +void * _cairo_hash_table_lookup (cairo_hash_table_t *hash_table, - cairo_hash_entry_t *key, - cairo_hash_entry_t **entry_return) + cairo_hash_entry_t *key) { - cairo_hash_entry_t **entry; + cairo_hash_entry_t *entry; + unsigned long table_size, i, idx, step; - /* See if we have an entry in the table already. */ - entry = _cairo_hash_table_lookup_internal (hash_table, key, FALSE); - if (ENTRY_IS_LIVE(*entry)) { - *entry_return = *entry; - return TRUE; - } + table_size = hash_table->arrangement->size; + idx = key->hash % table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry)) { + if (hash_table->keys_equal (key, entry)) + return entry; + } else if (ENTRY_IS_FREE (entry)) + return NULL; + + i = 1; + step = key->hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { + idx += step; + if (idx >= table_size) + idx -= table_size; - *entry_return = NULL; - return FALSE; + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry)) { + if (hash_table->keys_equal (key, entry)) + return entry; + } else if (ENTRY_IS_FREE (entry)) + return NULL; + } while (++i < table_size); + + return NULL; } /** * _cairo_hash_table_random_entry: * @hash_table: a hash table - * @predicate: a predicate function, or %NULL for any entry. + * @predicate: a predicate function. * * Find a random entry in the hash table satisfying the given - * @predicate. A %NULL @predicate is taken as equivalent to a function - * which always returns %TRUE, (eg. any entry in the table will do). + * @predicate. * * We use the same algorithm as the lookup algorithm to walk over the * entries in the hash table in a pseudo-random order. Walking @@ -407,36 +368,33 @@ void * _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, cairo_hash_predicate_func_t predicate) { - cairo_hash_entry_t **entry; + cairo_hash_entry_t *entry; unsigned long hash; unsigned long table_size, i, idx, step; - table_size = hash_table->arrangement->size; + assert (predicate != NULL); + table_size = hash_table->arrangement->size; hash = rand (); idx = hash % table_size; - step = 0; - - for (i = 0; i < table_size; ++i) - { - entry = &hash_table->entries[idx]; - if (ENTRY_IS_LIVE (*entry) && - (predicate == NULL || predicate (*entry))) - { - return *entry; - } - - if (step == 0) { - step = hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; - } + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry) && predicate (entry)) + return entry; + i = 1; + step = hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { idx += step; if (idx >= table_size) idx -= table_size; - } + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry) && predicate (entry)) + return entry; + } while (++i < table_size); return NULL; } @@ -448,8 +406,8 @@ _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, * * Insert the entry #key_and_value into the 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: There must not be an existing entry in the hash table + * with a matching key. * * WARNING: It is a fatal error to insert an element while * an iterator is running @@ -466,42 +424,61 @@ _cairo_hash_table_insert (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key_and_value) { 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); - - if (ENTRY_IS_LIVE(*entry)) - { - /* User is being bad, let's crash. */ - ASSERT_NOT_REACHED; - } - - *entry = key_and_value; hash_table->live_entries++; - status = _cairo_hash_table_resize (hash_table); - if (status) { + if (unlikely (status)) { /* abort the insert... */ - *entry = DEAD_ENTRY; hash_table->live_entries--; return status; } + *_cairo_hash_table_lookup_unique_key (hash_table, + key_and_value) = key_and_value; + return CAIRO_STATUS_SUCCESS; } +static cairo_hash_entry_t ** +_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + unsigned long table_size, i, idx, step; + cairo_hash_entry_t **entry; + + table_size = hash_table->arrangement->size; + idx = key->hash % table_size; + + entry = &hash_table->entries[idx]; + if (*entry == key) + return entry; + + i = 1; + step = key->hash % hash_table->arrangement->rehash; + if (step == 0) + step = 1; + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = &hash_table->entries[idx]; + if (*entry == key) + return entry; + } while (++i < table_size); + + ASSERT_NOT_REACHED; + return NULL; +} /** * _cairo_hash_table_remove: * @hash_table: a hash table * @key: key of entry to be removed * - * Remove an entry from the hash table which has a key that matches - * @key, if any (as determined by the keys_equal() function passed to - * _cairo_hash_table_create). + * Remove an entry from the hash table which points to @key. * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if out of memory. @@ -510,13 +487,7 @@ void _cairo_hash_table_remove (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key) { - cairo_hash_entry_t **entry; - - entry = _cairo_hash_table_lookup_internal (hash_table, key, FALSE); - if (! ENTRY_IS_LIVE(*entry)) - return; - - *entry = DEAD_ENTRY; + *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY; hash_table->live_entries--; /* Check for table resize. Don't do this when iterating as this will diff --git a/src/cairo-hull.c b/src/cairo-hull.c index 008ba7f..1fa919b 100644 --- a/src/cairo-hull.c +++ b/src/cairo-hull.c @@ -198,9 +198,12 @@ _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) cairo_hull_t *hull; int num_hull = *num_vertices; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (num_hull > ARRAY_LENGTH (hull_stack)) { hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t)); - if (hull == NULL) + if (unlikely (hull == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { hull = hull_stack; diff --git a/src/cairo-image-info-private.h b/src/cairo-image-info-private.h new file mode 100644 index 0000000..6e1c4ad --- /dev/null +++ b/src/cairo-image-info-private.h @@ -0,0 +1,63 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Adrian Johnson + * + * 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson <ajohnson@redneon.com> + */ + +#ifndef CAIRO_IMAGE_INFO_PRIVATE_H +#define CAIRO_IMAGE_INFO_PRIVATE_H + +#include "cairoint.h" + +typedef struct _cairo_image_info { + int width; + int height; + int num_components; + int bits_per_component; +} cairo_image_info_t; + +cairo_private cairo_int_status_t +_cairo_image_info_get_jpeg_info (cairo_image_info_t *info, + const unsigned char *data, + long length); + +cairo_private cairo_int_status_t +_cairo_image_info_get_jpx_info (cairo_image_info_t *info, + const unsigned char *data, + long length); + +cairo_private cairo_int_status_t +_cairo_image_info_get_png_info (cairo_image_info_t *info, + const unsigned char *data, + long length); + +#endif /* CAIRO_IMAGE_INFO_PRIVATE_H */ diff --git a/src/cairo-image-info.c b/src/cairo-image-info.c new file mode 100644 index 0000000..8fc7705 --- /dev/null +++ b/src/cairo-image-info.c @@ -0,0 +1,290 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Adrian Johnson + * + * 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson <ajohnson@redneon.com> + */ + +#include "cairoint.h" +#include "cairo-image-info-private.h" + +static uint32_t +_get_be32 (const unsigned char *p) +{ + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +/* JPEG (image/jpeg) + * + * http://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ + +/* Markers with no parameters. All other markers are followed by a two + * byte length of the parameters. */ +#define TEM 0x01 +#define RST_begin 0xd0 +#define RST_end 0xd7 +#define SOI 0xd8 +#define EOI 0xd9 + +/* Start of frame markers. */ +#define SOF0 0xc0 +#define SOF1 0xc1 +#define SOF2 0xc2 +#define SOF3 0xc3 +#define SOF5 0xc5 +#define SOF6 0xc6 +#define SOF7 0xc7 +#define SOF9 0xc9 +#define SOF10 0xca +#define SOF11 0xcb +#define SOF13 0xcd +#define SOF14 0xce +#define SOF15 0xcf + +static const unsigned char * +_jpeg_skip_segment (const unsigned char *p) +{ + int len; + + p++; + len = (p[0] << 8) | p[1]; + + return p + len; +} + +static void +_jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p) +{ + info->width = (p[6] << 8) + p[7]; + info->height = (p[4] << 8) + p[5]; + info->num_components = p[8]; + info->bits_per_component = p[3]; +} + +cairo_int_status_t +_cairo_image_info_get_jpeg_info (cairo_image_info_t *info, + const unsigned char *data, + long length) +{ + const unsigned char *p = data; + + while (p + 1 < data + length) { + if (*p != 0xff) + return CAIRO_INT_STATUS_UNSUPPORTED; + p++; + + switch (*p) { + /* skip fill bytes */ + case 0xff: + p++; + break; + + case TEM: + case SOI: + case EOI: + p++; + break; + + case SOF0: + case SOF1: + case SOF2: + case SOF3: + case SOF5: + case SOF6: + case SOF7: + case SOF9: + case SOF10: + case SOF11: + case SOF13: + case SOF14: + case SOF15: + /* Start of frame found. Extract the image parameters. */ + if (p + 8 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _jpeg_extract_info (info, p); + return CAIRO_STATUS_SUCCESS; + + default: + if (*p >= RST_begin && *p <= RST_end) { + p++; + break; + } + + if (p + 2 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p = _jpeg_skip_segment (p); + break; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/* JPEG 2000 (image/jp2) + * + * http://www.jpeg.org/public/15444-1annexi.pdf + */ + +#define JPX_FILETYPE 0x66747970 +#define JPX_JP2_HEADER 0x6A703268 +#define JPX_IMAGE_HEADER 0x69686472 + +static const unsigned char _jpx_signature[] = { + 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a +}; + +static const unsigned char * +_jpx_next_box (const unsigned char *p) +{ + return p + _get_be32 (p); +} + +static const unsigned char * +_jpx_get_box_contents (const unsigned char *p) +{ + return p + 8; +} + +static cairo_bool_t +_jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type) +{ + uint32_t length; + + if (p + 8 < end) { + length = _get_be32 (p); + if (_get_be32 (p + 4) == type && p + length < end) + return TRUE; + } + + return FALSE; +} + +static const unsigned char * +_jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type) +{ + while (p < end) { + if (_jpx_match_box (p, end, type)) + return p; + p = _jpx_next_box (p); + } + + return NULL; +} + +static void +_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) +{ + info->height = _get_be32 (p); + info->width = _get_be32 (p + 4); + info->num_components = (p[8] << 8) + p[9]; + info->bits_per_component = p[10]; +} + +cairo_int_status_t +_cairo_image_info_get_jpx_info (cairo_image_info_t *info, + const unsigned char *data, + long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + + /* First 12 bytes must be the JPEG 2000 signature box. */ + if (length < ARRAY_LENGTH(_jpx_signature) || + memcmp(p, _jpx_signature, ARRAY_LENGTH(_jpx_signature)) != 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += ARRAY_LENGTH(_jpx_signature); + + /* Next box must be a File Type Box */ + if (! _jpx_match_box (p, end, JPX_FILETYPE)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p = _jpx_next_box (p); + + /* Locate the JP2 header box. */ + p = _jpx_find_box (p, end, JPX_JP2_HEADER); + if (!p) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Step into the JP2 header box. First box must be the Image + * Header */ + p = _jpx_get_box_contents (p); + if (! _jpx_match_box (p, end, JPX_IMAGE_HEADER)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Get the image info */ + p = _jpx_get_box_contents (p); + _jpx_extract_info (p, info); + + return CAIRO_STATUS_SUCCESS; +} + +/* PNG (image/png) + * + * http://www.w3.org/TR/2003/REC-PNG-20031110/ + */ + +#define PNG_IHDR 0x49484452 + +static const unsigned char _png_magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + +cairo_int_status_t +_cairo_image_info_get_png_info (cairo_image_info_t *info, + const unsigned char *data, + long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + + if (length < 8 || memcmp (data, _png_magic, 8) != 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 8; + + /* The first chunk must be IDHR. IDHR has 13 bytes of data plus + * the 12 bytes of overhead for the chunk. */ + if (p + 13 + 12 > end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 4; + if (_get_be32 (p) != PNG_IHDR) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 4; + info->width = _get_be32 (p); + p += 4; + info->height = _get_be32 (p); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 21c9be4..8c9df5a 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -131,7 +131,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, cairo_image_surface_t *surface; surface = malloc (sizeof (cairo_image_surface_t)); - if (surface == NULL) + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_image_surface_backend, @@ -203,7 +203,7 @@ _pixman_format_from_masks (cairo_format_masks_t *masks, } /* A mask consisting of N bits set to 1. */ -#define MASK(N) ((1 << (N))-1) +#define MASK(N) ((1UL << (N))-1) void _pixman_format_to_masks (pixman_format_code_t format, @@ -327,7 +327,7 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, pixman_image = pixman_image_create_bits (pixman_format, width, height, (uint32_t *) data, stride); - if (pixman_image == NULL) + if (unlikely (pixman_image == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface = _cairo_image_surface_create_for_pixman_image (pixman_image, @@ -569,7 +569,7 @@ cairo_image_surface_get_format (cairo_surface_t *surface) if (! _cairo_surface_is_image (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; + return CAIRO_FORMAT_INVALID; } return image_surface->format; @@ -731,7 +731,7 @@ _cairo_image_surface_finish (void *abstract_surface) void _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) { - surface->owns_data = 1; + surface->owns_data = TRUE; } static cairo_status_t @@ -784,6 +784,7 @@ _cairo_image_surface_release_dest_image (void *abstract_surfa static cairo_status_t _cairo_image_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -806,11 +807,12 @@ _cairo_image_surface_clone_similar (void *abstract_surface, static cairo_status_t _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, - const cairo_matrix_t *matrix) + const cairo_matrix_t *matrix, + double xc, double yc) { pixman_transform_t pixman_transform; - _cairo_matrix_to_pixman_matrix (matrix, &pixman_transform); + _cairo_matrix_to_pixman_matrix (matrix, &pixman_transform, xc, yc); if (! pixman_image_set_transform (surface->pixman_image, &pixman_transform)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -862,12 +864,14 @@ _cairo_image_surface_set_filter (cairo_image_surface_t *surface, static cairo_status_t _cairo_image_surface_set_attributes (cairo_image_surface_t *surface, - cairo_surface_attributes_t *attributes) + cairo_surface_attributes_t *attributes, + double xc, double yc) { cairo_int_status_t status; - status = _cairo_image_surface_set_matrix (surface, &attributes->matrix); - if (status) + status = _cairo_image_surface_set_matrix (surface, &attributes->matrix, + xc, yc); + if (unlikely (status)) return status; switch (attributes->extend) { @@ -886,7 +890,7 @@ _cairo_image_surface_set_attributes (cairo_image_surface_t *surface, } status = _cairo_image_surface_set_filter (surface, attributes->filter); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -939,8 +943,8 @@ _pixman_operator (cairo_operator_t op) static cairo_int_status_t _cairo_image_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -959,23 +963,29 @@ _cairo_image_surface_composite (cairo_operator_t op, status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, mask_x, mask_y, width, height, + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, (cairo_surface_t **) &mask, &src_attr, &mask_attr); - if (status) + if (unlikely (status)) return status; - status = _cairo_image_surface_set_attributes (src, &src_attr); - if (status) - goto CLEANUP_SURFACES; + status = _cairo_image_surface_set_attributes (src, &src_attr, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) + goto CLEANUP_SURFACES; if (mask) { - status = _cairo_image_surface_set_attributes (mask, &mask_attr); - if (status) + status = _cairo_image_surface_set_attributes (mask, &mask_attr, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) goto CLEANUP_SURFACES; pixman_image_composite (_pixman_operator (op), @@ -1037,6 +1047,9 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + pixman_color.red = color->red_short; pixman_color.green = color->green_short; pixman_color.blue = color->blue_short; @@ -1044,7 +1057,7 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, if (num_rects > ARRAY_LENGTH (stack_rects)) { pixman_rects = _cairo_malloc_ab (num_rects, sizeof (pixman_rectangle16_t)); - if (pixman_rects == NULL) + if (unlikely (pixman_rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1057,10 +1070,11 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, /* XXX: pixman_fill_rectangles() should be implemented */ if (! pixman_image_fill_rectangles (_pixman_operator (op), - surface->pixman_image, - &pixman_color, - num_rects, - pixman_rects)) { + surface->pixman_image, + &pixman_color, + num_rects, + pixman_rects)) + { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1070,9 +1084,45 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, return status; } +static pixman_format_code_t +_pixman_mask_format_from_antialias (cairo_antialias_t antialias) +{ + if (antialias == CAIRO_ANTIALIAS_NONE) + return PIXMAN_a1; + return PIXMAN_a8; +} + +static void +_pixman_add_trapezoids (pixman_image_t *image, + int dst_x, int dst_y, + const cairo_trapezoid_t *traps, + int num_traps) +{ + while (num_traps--) { + pixman_trapezoid_t trap; + + trap.top = _cairo_fixed_to_16_16 (traps->top); + trap.bottom = _cairo_fixed_to_16_16 (traps->bottom); + + trap.left.p1.x = _cairo_fixed_to_16_16 (traps->left.p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (traps->left.p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (traps->left.p2.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (traps->left.p2.y); + + trap.right.p1.x = _cairo_fixed_to_16_16 (traps->right.p1.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (traps->right.p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (traps->right.p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (traps->right.p2.y); + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + + traps++; + } +} + static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, @@ -1087,37 +1137,14 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, cairo_surface_attributes_t attributes; cairo_image_surface_t *dst = abstract_dst; cairo_image_surface_t *src; - cairo_int_status_t status; + cairo_int_status_t status; pixman_image_t *mask; - pixman_format_code_t format; - uint32_t *mask_data; - pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; - pixman_trapezoid_t *pixman_traps = stack_traps; - int mask_stride; - int i; if (height == 0 || width == 0) return CAIRO_STATUS_SUCCESS; - /* Convert traps to pixman traps */ - if (num_traps > ARRAY_LENGTH (stack_traps)) { - pixman_traps = _cairo_malloc_ab (num_traps, sizeof (pixman_trapezoid_t)); - if (pixman_traps == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < num_traps; i++) { - pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); - pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); - pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); - pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); - pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); - pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); - pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); - pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); - pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); - pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); - } + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* Special case adding trapezoids onto a mask surface; we want to avoid * creating an intermediate temporary mask unnecessarily. @@ -1138,53 +1165,33 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, ! dst->has_clip && antialias != CAIRO_ANTIALIAS_NONE) { - pixman_add_trapezoids (dst->pixman_image, 0, 0, - num_traps, pixman_traps); - status = CAIRO_STATUS_SUCCESS; - goto finish; + _pixman_add_trapezoids (dst->pixman_image, 0, 0, traps, num_traps); + return CAIRO_STATUS_SUCCESS; } status = _cairo_pattern_acquire_surface (pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, width, height, + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attributes); - if (status) - goto finish; + if (unlikely (status)) + return status; - status = _cairo_image_surface_set_attributes (src, &attributes); - if (status) + status = _cairo_image_surface_set_attributes (src, &attributes, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) goto CLEANUP_SOURCE; - switch (antialias) { - case CAIRO_ANTIALIAS_NONE: - format = PIXMAN_a1; - mask_stride = ((width + 31) / 8) & ~0x03; - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_DEFAULT: - default: - format = PIXMAN_a8; - mask_stride = (width + 3) & ~3; - break; - } - - /* The image must be initially transparent */ - mask_data = calloc (mask_stride, height); - if (mask_data == NULL) { + mask = pixman_image_create_bits (_pixman_mask_format_from_antialias (antialias), + width, height, NULL, 0); + if (unlikely (mask == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_SOURCE; } - mask = pixman_image_create_bits (format, width, height, - mask_data, mask_stride); - if (mask == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_IMAGE_DATA; - } - - pixman_add_trapezoids (mask, - dst_x, - dst_y, - num_traps, pixman_traps); + _pixman_add_trapezoids (mask, dst_x, dst_y, traps, num_traps); pixman_image_composite (_pixman_operator (op), src->pixman_image, @@ -1196,35 +1203,242 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, dst_x, dst_y, width, height); + pixman_image_unref (mask); + if (! _cairo_operator_bounded_by_mask (op)) status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, src->width, src->height, + &attributes, + src->width, src->height, width, height, src_x, src_y, 0, 0, dst_x, dst_y, width, height); - pixman_image_unref (mask); - - CLEANUP_IMAGE_DATA: - free (mask_data); CLEANUP_SOURCE: _cairo_pattern_release_surface (pattern, &src->base, &attributes); - finish: - if (pixman_traps != stack_traps) - free (pixman_traps); - return status; } +typedef struct _cairo_image_surface_span_renderer { + cairo_span_renderer_t base; + + cairo_operator_t op; + const cairo_pattern_t *pattern; + cairo_antialias_t antialias; + + cairo_image_surface_t *src; + cairo_surface_attributes_t src_attributes; + cairo_image_surface_t *mask; + cairo_image_surface_t *dst; + + cairo_composite_rectangles_t composite_rectangles; +} cairo_image_surface_span_renderer_t; + +static cairo_status_t +_cairo_image_surface_span_renderer_render_row ( + void *abstract_renderer, + int y, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_surface_span_renderer_t *renderer = abstract_renderer; + int xmin = renderer->composite_rectangles.mask.x; + int xmax = xmin + renderer->composite_rectangles.width; + uint8_t *row; + int prev_x = xmin; + int prev_alpha = 0; + unsigned i; + + /* Make sure we're within y-range. */ + y -= renderer->composite_rectangles.mask.y; + if (y < 0 || y >= renderer->composite_rectangles.height) + return CAIRO_STATUS_SUCCESS; + + row = (uint8_t*)(renderer->mask->data) + y*(size_t)renderer->mask->stride - xmin; + + /* Find the first span within x-range. */ + for (i=0; i < num_spans && spans[i].x < xmin; i++) {} + if (i>0) + prev_alpha = spans[i-1].coverage; + + /* Set the intermediate spans. */ + for (; i < num_spans; i++) { + int x = spans[i].x; + + if (x >= xmax) + break; + + if (prev_alpha != 0) { + /* We implement setting rendering the most common single + * pixel wide span case to avoid the overhead of a memset + * call. Open coding setting longer spans didn't show a + * noticeable improvement over memset. */ + if (x == prev_x + 1) { + row[prev_x] = prev_alpha; + } + else { + memset(row + prev_x, prev_alpha, x - prev_x); + } + } + + prev_x = x; + prev_alpha = spans[i].coverage; + } + + if (prev_alpha != 0 && prev_x < xmax) { + memset(row + prev_x, prev_alpha, xmax - prev_x); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_surface_span_renderer_destroy (void *abstract_renderer) +{ + cairo_image_surface_span_renderer_t *renderer = abstract_renderer; + if (!renderer) return; + + if (renderer->src != NULL) { + _cairo_pattern_release_surface (renderer->pattern, + &renderer->src->base, + &renderer->src_attributes); + } + + if (renderer->mask != NULL) + cairo_surface_destroy (&renderer->mask->base); + + free (renderer); +} + +static cairo_status_t +_cairo_image_surface_span_renderer_finish (void *abstract_renderer) +{ + cairo_image_surface_span_renderer_t *renderer = abstract_renderer; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (renderer->src == NULL || renderer->mask == NULL) + return CAIRO_STATUS_SUCCESS; + + status = cairo_surface_status (&renderer->mask->base); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; + cairo_image_surface_t *src = renderer->src; + cairo_image_surface_t *dst = renderer->dst; + cairo_surface_attributes_t *src_attributes = &renderer->src_attributes; + int width = rects->width; + int height = rects->height; + + pixman_image_composite (_pixman_operator (renderer->op), + src->pixman_image, + renderer->mask->pixman_image, + dst->pixman_image, + rects->src.x + src_attributes->x_offset, + rects->src.y + src_attributes->y_offset, + 0, 0, /* mask.x, mask.y */ + rects->dst.x, rects->dst.y, + width, height); + + if (! _cairo_operator_bounded_by_mask (renderer->op)) + status = _cairo_surface_composite_shape_fixup_unbounded ( + &dst->base, + src_attributes, + src->width, src->height, + rects->width, rects->height, + rects->src.x, rects->src.y, + 0, 0, /* mask.x, mask.y */ + rects->dst.x, rects->dst.y, + rects->width, rects->height); + } + if (status != CAIRO_STATUS_SUCCESS) + return _cairo_span_renderer_set_error (abstract_renderer, + status); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_image_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + (void) op; + (void) pattern; + (void) abstract_dst; + (void) antialias; + (void) rects; + return TRUE; +} + +static cairo_span_renderer_t * +_cairo_image_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *abstract_dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + cairo_image_surface_t *dst = abstract_dst; + cairo_image_surface_span_renderer_t *renderer + = calloc(1, sizeof(*renderer)); + cairo_status_t status; + int width = rects->width; + int height = rects->height; + + if (renderer == NULL) + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); + + renderer->base.destroy = _cairo_image_surface_span_renderer_destroy; + renderer->base.finish = _cairo_image_surface_span_renderer_finish; + renderer->base.render_row = + _cairo_image_surface_span_renderer_render_row; + renderer->op = op; + renderer->pattern = pattern; + renderer->antialias = antialias; + renderer->dst = dst; + + renderer->composite_rectangles = *rects; + + status = _cairo_pattern_acquire_surface ( + renderer->pattern, &renderer->dst->base, + CAIRO_CONTENT_COLOR_ALPHA, + rects->src.x, rects->src.y, + width, height, + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) &renderer->src, + &renderer->src_attributes); + if (status) + goto unwind; + + status = _cairo_image_surface_set_attributes ( + renderer->src, &renderer->src_attributes, + rects->dst.x + width/2, rects->dst.y + height/2); + if (status) + goto unwind; + + /* TODO: support rendering to A1 surfaces (or: go add span + * compositing to pixman.) */ + renderer->mask = (cairo_image_surface_t *) + cairo_image_surface_create (CAIRO_FORMAT_A8, + width, height); + + status = cairo_surface_status (&renderer->mask->base); + + unwind: + if (status != CAIRO_STATUS_SUCCESS) { + _cairo_image_surface_span_renderer_destroy (renderer); + return _cairo_span_renderer_create_in_error (status); + } + return &renderer->base; +} + cairo_int_status_t _cairo_image_surface_set_clip_region (void *abstract_surface, cairo_region_t *region) { cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface; - if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn)) + if (! pixman_image_set_clip_region32 (surface->pixman_image, region? ®ion->rgn : NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); surface->has_clip = region != NULL; @@ -1293,6 +1507,8 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { _cairo_image_surface_composite, _cairo_image_surface_fill_rectangles, _cairo_image_surface_composite_trapezoids, + _cairo_image_surface_create_span_renderer, + _cairo_image_surface_check_span_renderer, NULL, /* copy_page */ NULL, /* show_page */ _cairo_image_surface_set_clip_region, @@ -1319,35 +1535,41 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { /* A convenience function for when one needs to coerce an image * surface to an alternate format. */ cairo_image_surface_t * -_cairo_image_surface_clone (cairo_image_surface_t *surface, - cairo_format_t format) +_cairo_image_surface_coerce (cairo_image_surface_t *surface, + cairo_format_t format) { cairo_image_surface_t *clone; + cairo_surface_pattern_t pattern; cairo_status_t status; - cairo_t *cr; - double x, y; + + status = surface->base.status; + if (unlikely (status)) + return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); + + if (surface->format == format) + return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); clone = (cairo_image_surface_t *) - cairo_image_surface_create (format, - surface->width, surface->height); - - cairo_surface_get_device_offset (&surface->base, &x, &y); - cairo_surface_set_device_offset (&clone->base, x, y); - clone->transparency = CAIRO_IMAGE_UNKNOWN; - - /* XXX Use _cairo_surface_composite directly */ - cr = cairo_create (&clone->base); - cairo_set_source_surface (cr, &surface->base, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - status = cairo_status (cr); - cairo_destroy (cr); - - if (status) { + cairo_image_surface_create (format, surface->width, surface->height); + if (unlikely (clone->base.status)) + return clone; + + _cairo_pattern_init_for_surface (&pattern, &surface->base); + status = _cairo_surface_paint (&clone->base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { cairo_surface_destroy (&clone->base); - return (cairo_image_surface_t *) _cairo_surface_create_in_error (status); + return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); } + clone->base.device_transform = + surface->base.device_transform; + clone->base.device_transform_inverse = + surface->base.device_transform_inverse; + return clone; } @@ -1359,16 +1581,19 @@ _cairo_image_analyze_transparency (cairo_image_surface_t *image) if (image->transparency != CAIRO_IMAGE_UNKNOWN) return image->transparency; - if (image->format == CAIRO_FORMAT_RGB24) { - image->transparency = CAIRO_IMAGE_IS_OPAQUE; - return CAIRO_IMAGE_IS_OPAQUE; - } + if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) + return image->transparency = CAIRO_IMAGE_IS_OPAQUE; - if (image->format != CAIRO_FORMAT_ARGB32) { - image->transparency = CAIRO_IMAGE_HAS_ALPHA; - return CAIRO_IMAGE_HAS_ALPHA; + if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) { + if (image->format == CAIRO_FORMAT_A1) + return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + else + return image->transparency = CAIRO_IMAGE_HAS_ALPHA; } + if (image->format != CAIRO_FORMAT_ARGB32) + return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + image->transparency = CAIRO_IMAGE_IS_OPAQUE; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); @@ -1376,8 +1601,7 @@ _cairo_image_analyze_transparency (cairo_image_surface_t *image) for (x = 0; x < image->width; x++, pixel++) { int a = (*pixel & 0xff000000) >> 24; if (a > 0 && a < 255) { - image->transparency = CAIRO_IMAGE_HAS_ALPHA; - return CAIRO_IMAGE_HAS_ALPHA; + return image->transparency = CAIRO_IMAGE_HAS_ALPHA; } else if (a == 0) { image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } diff --git a/src/cairo-lzw.c b/src/cairo-lzw.c index 1241225..a4fdf73 100644 --- a/src/cairo-lzw.c +++ b/src/cairo-lzw.c @@ -73,7 +73,7 @@ _lzw_buf_init (lzw_buf_t *buf, int size) buf->pending_bits = 0; buf->data = malloc (size); - if (buf->data == NULL) { + if (unlikely (buf->data == NULL)) { buf->data_size = 0; buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return; @@ -98,7 +98,7 @@ _lzw_buf_grow (lzw_buf_t *buf) if (new_size / 2 == buf->data_size) new_data = realloc (buf->data, new_size); - if (new_data == NULL) { + if (unlikely (new_data == NULL)) { free (buf->data); buf->data_size = 0; buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -137,7 +137,7 @@ _lzw_buf_store_bits (lzw_buf_t *buf, uint16_t value, int num_bits) while (buf->pending_bits >= 8) { if (buf->num_data >= buf->data_size) { status = _lzw_buf_grow (buf); - if (status) + if (unlikely (status)) return; } buf->data[buf->num_data++] = buf->pending >> (buf->pending_bits - 8); @@ -167,7 +167,7 @@ _lzw_buf_store_pending (lzw_buf_t *buf) if (buf->num_data >= buf->data_size) { status = _lzw_buf_grow (buf); - if (status) + if (unlikely (status)) return; } diff --git a/src/cairo-malloc-private.h b/src/cairo-malloc-private.h index f8094f9..d812058 100644 --- a/src/cairo-malloc-private.h +++ b/src/cairo-malloc-private.h @@ -39,6 +39,13 @@ #include "cairo-wideint-private.h" +#if HAVE_MEMFAULT +#include <memfault.h> +#define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT() +#else +#define CAIRO_INJECT_FAULT() 0 +#endif + /** * _cairo_malloc: * @size: size in bytes diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index 6a29aec..b9e7290 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -371,6 +371,46 @@ _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, double min_x, max_x; double min_y, max_y; + if (_cairo_matrix_is_identity (matrix)) { + if (is_tight) + *is_tight = TRUE; + + return; + } + + if (matrix->xy == 0. && matrix->yx == 0.) { + /* non-rotation/skew matrix, just map the two extreme points */ + quad_x[0] = *x1; + quad_y[0] = *y1; + cairo_matrix_transform_distance (matrix, &quad_x[0], &quad_y[0]); + + quad_x[1] = *x2; + quad_y[1] = *y2; + cairo_matrix_transform_distance (matrix, &quad_x[1], &quad_y[1]); + + if (quad_x[0] < quad_x[1]) { + *x1 = quad_x[0] + matrix->x0; + *x2 = quad_x[1] + matrix->x0; + } else { + *x1 = quad_x[1] + matrix->x0; + *x2 = quad_x[0] + matrix->x0; + } + + if (quad_y[0] < quad_y[1]) { + *y1 = quad_y[0] + matrix->y0; + *y2 = quad_y[1] + matrix->y0; + } else { + *y1 = quad_y[1] + matrix->y0; + *y2 = quad_y[0] + matrix->y0; + } + + if (is_tight) + *is_tight = TRUE; + + return; + } + + /* general matrix */ quad_x[0] = *x1; quad_y[0] = *y1; cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]); @@ -406,7 +446,7 @@ _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, *y1 = min_y; *x2 = max_x; *y2 = max_y; - + if (is_tight) { /* it's tight if and only if the four corner points form an axis-aligned rectangle. @@ -473,7 +513,7 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) * cairo_matrix_invert: * @matrix: a #cairo_matrix_t * - * Changes @matrix to be the inverse of it's original value. Not + * Changes @matrix to be the inverse of its original value. Not * all transformation matrices have inverses; if the matrix * collapses points together (it is <firstterm>degenerate</firstterm>), * then it has no inverse and this function will fail. @@ -799,7 +839,8 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) /* determine the length of the major axis of a circle of the given radius after applying the transformation matrix. */ double -_cairo_matrix_transformed_circle_major_axis (cairo_matrix_t *matrix, double radius) +_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, + double radius) { double a, b, c, d, f, g, h, i, j; @@ -825,7 +866,9 @@ _cairo_matrix_transformed_circle_major_axis (cairo_matrix_t *matrix, double radi void _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform) + pixman_transform_t *pixman_transform, + double xc, + double yc) { static const pixman_transform_t pixman_identity_transform = {{ {1 << 16, 0, 0}, @@ -835,11 +878,9 @@ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, if (_cairo_matrix_is_identity (matrix)) { *pixman_transform = pixman_identity_transform; - } - else { - cairo_matrix_t inv = *matrix; - double x = 0, y = 0; - pixman_vector_t vector; + } else { + cairo_matrix_t inv; + unsigned max_iterations; pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); @@ -859,29 +900,51 @@ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, * for cairo, while pixman uses rounded versions of xx ... yy. * This error increases as a and b get larger. * - * To compensate for this, we fix the point (0, 0) in pattern + * To compensate for this, we fix the point (xc, yc) in pattern * space and adjust pixman's transform to agree with cairo's at - * that point. */ + * that point. + */ + + if (_cairo_matrix_is_translation (matrix)) + return; /* Note: If we can't invert the transformation, skip the adjustment. */ + inv = *matrix; if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) return; - /* find the device space coordinate that maps to (0, 0) */ - cairo_matrix_transform_point (&inv, &x, &y); - - /* transform the resulting device space coordinate back - * to the pattern space, using pixman's transform */ - vector.vector[0] = _cairo_fixed_16_16_from_double (x); - vector.vector[1] = _cairo_fixed_16_16_from_double (y); - vector.vector[2] = 1 << 16; - - if (!pixman_transform_point_3d (pixman_transform, &vector)) - return; - - /* Ideally, the vector should now be (0, 0). We can now compensate - * for the resulting error */ - pixman_transform->matrix[0][2] -= vector.vector[0]; - pixman_transform->matrix[1][2] -= vector.vector[1]; + /* find the pattern space coordinate that maps to (xc, yc) */ + xc += .5; yc += .5; /* offset for the pixel centre */ + max_iterations = 5; + do { + double x,y; + pixman_vector_t vector; + cairo_fixed_16_16_t dx, dy; + + vector.vector[0] = _cairo_fixed_16_16_from_double (xc); + vector.vector[1] = _cairo_fixed_16_16_from_double (yc); + vector.vector[2] = 1 << 16; + + if (! pixman_transform_point_3d (pixman_transform, &vector)) + return; + + x = pixman_fixed_to_double (vector.vector[0]); + y = pixman_fixed_to_double (vector.vector[1]); + cairo_matrix_transform_point (&inv, &x, &y); + + /* Ideally, the vector should now be (xc, yc). + * We can now compensate for the resulting error. + */ + x -= xc; + y -= yc; + cairo_matrix_transform_distance (matrix, &x, &y); + dx = _cairo_fixed_16_16_from_double (x); + dy = _cairo_fixed_16_16_from_double (y); + pixman_transform->matrix[0][2] -= dx; + pixman_transform->matrix[1][2] -= dy; + + if (dx == 0 && dy == 0) + break; + } while (--max_iterations); } } diff --git a/src/cairo-meta-surface-private.h b/src/cairo-meta-surface-private.h index 9a1b169..8d5e096 100644 --- a/src/cairo-meta-surface-private.h +++ b/src/cairo-meta-surface-private.h @@ -67,6 +67,7 @@ typedef enum { typedef struct _cairo_command_header { cairo_command_type_t type; cairo_meta_region_type_t region; + cairo_rectangle_int_t extents; } cairo_command_header_t; typedef struct _cairo_command_paint { diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c index b86cb51..d505adc 100644 --- a/src/cairo-meta-surface.c +++ b/src/cairo-meta-surface.c @@ -84,7 +84,7 @@ _cairo_meta_surface_create (cairo_content_t content, cairo_meta_surface_t *meta; meta = malloc (sizeof (cairo_meta_surface_t)); - if (meta == NULL) + if (unlikely (meta == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, @@ -197,7 +197,7 @@ _cairo_meta_surface_acquire_source_image (void *abstract_surface, surface->height_pixels); status = _cairo_meta_surface_replay (&surface->base, image); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (image); return status; } @@ -219,26 +219,31 @@ _cairo_meta_surface_release_source_image (void *abstract_surface, static cairo_int_status_t _cairo_meta_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_meta_surface_t *meta = abstract_surface; cairo_command_paint_t *command; command = malloc (sizeof (cairo_command_paint_t)); - if (command == NULL) + if (unlikely (command == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); command->header.type = CAIRO_COMMAND_PAINT; command->header.region = CAIRO_META_REGION_ALL; + command->header.extents.x = 0; + command->header.extents.y = 0; + command->header.extents.width = meta->width_pixels; + command->header.extents.height = meta->height_pixels; command->op = op; status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (status) + if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_array_append (&meta->commands, &command); - if (status) + if (unlikely (status)) goto CLEANUP_SOURCE; /* An optimisation that takes care to not replay what was done @@ -259,31 +264,36 @@ _cairo_meta_surface_paint (void *abstract_surface, static cairo_int_status_t _cairo_meta_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_meta_surface_t *meta = abstract_surface; cairo_command_mask_t *command; command = malloc (sizeof (cairo_command_mask_t)); - if (command == NULL) + if (unlikely (command == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); command->header.type = CAIRO_COMMAND_MASK; command->header.region = CAIRO_META_REGION_ALL; + command->header.extents.x = 0; + command->header.extents.y = 0; + command->header.extents.width = meta->width_pixels; + command->header.extents.height = meta->height_pixels; command->op = op; status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (status) + if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->mask.base, mask); - if (status) + if (unlikely (status)) goto CLEANUP_SOURCE; status = _cairo_array_append (&meta->commands, &command); - if (status) + if (unlikely (status)) goto CLEANUP_MASK; return CAIRO_STATUS_SUCCESS; @@ -300,36 +310,41 @@ _cairo_meta_surface_mask (void *abstract_surface, static cairo_int_status_t _cairo_meta_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_meta_surface_t *meta = abstract_surface; cairo_command_stroke_t *command; command = malloc (sizeof (cairo_command_stroke_t)); - if (command == NULL) + if (unlikely (command == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); command->header.type = CAIRO_COMMAND_STROKE; command->header.region = CAIRO_META_REGION_ALL; + command->header.extents.x = 0; + command->header.extents.y = 0; + command->header.extents.width = meta->width_pixels; + command->header.extents.height = meta->height_pixels; command->op = op; status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (status) + if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_path_fixed_init_copy (&command->path, path); - if (status) + if (unlikely (status)) goto CLEANUP_SOURCE; status = _cairo_stroke_style_init_copy (&command->style, style); - if (status) + if (unlikely (status)) goto CLEANUP_PATH; command->ctm = *ctm; @@ -338,7 +353,7 @@ _cairo_meta_surface_stroke (void *abstract_surface, command->antialias = antialias; status = _cairo_array_append (&meta->commands, &command); - if (status) + if (unlikely (status)) goto CLEANUP_STYLE; return CAIRO_STATUS_SUCCESS; @@ -357,30 +372,35 @@ _cairo_meta_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_meta_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_meta_surface_t *meta = abstract_surface; cairo_command_fill_t *command; command = malloc (sizeof (cairo_command_fill_t)); - if (command == NULL) + if (unlikely (command == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); command->header.type = CAIRO_COMMAND_FILL; command->header.region = CAIRO_META_REGION_ALL; + command->header.extents.x = 0; + command->header.extents.y = 0; + command->header.extents.width = meta->width_pixels; + command->header.extents.height = meta->height_pixels; command->op = op; status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (status) + if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_path_fixed_init_copy (&command->path, path); - if (status) + if (unlikely (status)) goto CLEANUP_SOURCE; command->fill_rule = fill_rule; @@ -388,7 +408,7 @@ _cairo_meta_surface_fill (void *abstract_surface, command->antialias = antialias; status = _cairo_array_append (&meta->commands, &command); - if (status) + if (unlikely (status)) goto CLEANUP_PATH; return CAIRO_STATUS_SUCCESS; @@ -411,7 +431,7 @@ _cairo_meta_surface_has_show_text_glyphs (void *abstract_surface) static cairo_int_status_t _cairo_meta_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -419,22 +439,27 @@ _cairo_meta_surface_show_text_glyphs (void *abstract_surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_meta_surface_t *meta = abstract_surface; cairo_command_show_text_glyphs_t *command; command = malloc (sizeof (cairo_command_show_text_glyphs_t)); - if (command == NULL) + if (unlikely (command == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); command->header.type = CAIRO_COMMAND_SHOW_TEXT_GLYPHS; command->header.region = CAIRO_META_REGION_ALL; + command->header.extents.x = 0; + command->header.extents.y = 0; + command->header.extents.width = meta->width_pixels; + command->header.extents.height = meta->height_pixels; command->op = op; status = _cairo_pattern_init_snapshot (&command->source.base, source); - if (status) + if (unlikely (status)) goto CLEANUP_COMMAND; command->utf8 = NULL; @@ -446,7 +471,7 @@ _cairo_meta_surface_show_text_glyphs (void *abstract_surface, if (utf8_len) { command->utf8 = malloc (utf8_len); - if (command->utf8 == NULL) { + if (unlikely (command->utf8 == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; } @@ -454,7 +479,7 @@ _cairo_meta_surface_show_text_glyphs (void *abstract_surface, } if (num_glyphs) { command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); - if (command->glyphs == NULL) { + if (unlikely (command->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; } @@ -462,7 +487,7 @@ _cairo_meta_surface_show_text_glyphs (void *abstract_surface, } if (num_clusters) { command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); - if (command->clusters == NULL) { + if (unlikely (command->clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; } @@ -474,7 +499,7 @@ _cairo_meta_surface_show_text_glyphs (void *abstract_surface, command->scaled_font = cairo_scaled_font_reference (scaled_font); status = _cairo_array_append (&meta->commands, &command); - if (status) + if (unlikely (status)) goto CLEANUP_SCALED_FONT; return CAIRO_STATUS_SUCCESS; @@ -512,12 +537,11 @@ _cairo_meta_surface_snapshot (void *abstract_other) cairo_meta_surface_t *meta; meta = malloc (sizeof (cairo_meta_surface_t)); - if (meta == NULL) + if (unlikely (meta == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, other->base.content); - meta->base.is_snapshot = TRUE; meta->width_pixels = other->width_pixels; meta->height_pixels = other->height_pixels; @@ -542,7 +566,7 @@ _cairo_meta_surface_intersect_clip_path (void *dst, cairo_status_t status; command = malloc (sizeof (cairo_command_intersect_clip_path_t)); - if (command == NULL) + if (unlikely (command == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); command->header.type = CAIRO_COMMAND_INTERSECT_CLIP_PATH; @@ -550,7 +574,7 @@ _cairo_meta_surface_intersect_clip_path (void *dst, if (path) { status = _cairo_path_fixed_init_copy (&command->path, path); - if (status) { + if (unlikely (status)) { free (command); return status; } @@ -565,7 +589,7 @@ _cairo_meta_surface_intersect_clip_path (void *dst, command->antialias = antialias; status = _cairo_array_append (&meta->commands, &command); - if (status) { + if (unlikely (status)) { if (path) _cairo_path_fixed_fini (&command->path); free (command); @@ -622,6 +646,8 @@ static const cairo_surface_backend_t cairo_meta_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ @@ -651,6 +677,7 @@ static const cairo_surface_backend_t cairo_meta_surface_backend = { NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ _cairo_meta_surface_has_show_text_glyphs, _cairo_meta_surface_show_text_glyphs @@ -741,7 +768,7 @@ _cairo_meta_surface_get_path (cairo_surface_t *surface, ASSERT_NOT_REACHED; } - if (status) + if (unlikely (status)) break; } @@ -789,14 +816,14 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, * ensure the current clip gets set on the surface. */ if (command->header.type != CAIRO_COMMAND_INTERSECT_CLIP_PATH) { status = _cairo_surface_set_clip (target, &clip); - if (status) + if (unlikely (status)) break; } dev_path = _cairo_command_get_path (command); if (dev_path && has_device_transform) { status = _cairo_path_fixed_init_copy (&path_copy, dev_path); - if (status) + if (unlikely (status)) break; _cairo_path_fixed_transform (&path_copy, device_transform); dev_path = &path_copy; @@ -806,13 +833,13 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, case CAIRO_COMMAND_PAINT: status = _cairo_surface_paint (target, command->paint.op, - &command->paint.source.base); + &command->paint.source.base, &command->header.extents); break; case CAIRO_COMMAND_MASK: status = _cairo_surface_mask (target, command->mask.op, &command->mask.source.base, - &command->mask.mask.base); + &command->mask.mask.base, &command->header.extents); break; case CAIRO_COMMAND_STROKE: { @@ -834,7 +861,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, &dev_ctm, &dev_ctm_inverse, command->stroke.tolerance, - command->stroke.antialias); + command->stroke.antialias, &command->header.extents); break; } case CAIRO_COMMAND_FILL: @@ -881,7 +908,8 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, &dev_ctm, &dev_ctm_inverse, stroke_command->stroke.tolerance, - stroke_command->stroke.antialias); + stroke_command->stroke.antialias, + &stroke_command->header.extents); i++; } else status = _cairo_surface_fill (target, @@ -890,7 +918,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, dev_path, command->fill.fill_rule, command->fill.tolerance, - command->fill.antialias); + command->fill.antialias, &command->header.extents); break; } case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: @@ -904,7 +932,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, * copy the array before handing it to the backend. */ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (dev_glyphs == NULL) { + if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); break; } @@ -927,7 +955,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, dev_glyphs, num_glyphs, command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, command->show_text_glyphs.cluster_flags, - command->show_text_glyphs.scaled_font); + command->show_text_glyphs.scaled_font, &command->header.extents); free (dev_glyphs); break; @@ -960,7 +988,7 @@ _cairo_meta_surface_replay_internal (cairo_surface_t *surface, } } - if (status) + if (unlikely (status)) break; } diff --git a/src/cairo-misc.c b/src/cairo-misc.c index 27050a2..8d9557e 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -58,13 +58,13 @@ cairo_status_to_string (cairo_status_t status) { switch (status) { case CAIRO_STATUS_SUCCESS: - return "success"; + return "no error has occurred"; case CAIRO_STATUS_NO_MEMORY: return "out of memory"; case CAIRO_STATUS_INVALID_RESTORE: - return "cairo_restore without matching cairo_save"; + return "cairo_restore() without matching cairo_save()"; case CAIRO_STATUS_INVALID_POP_GROUP: - return "cairo_pop_group without matching cairo_push_group"; + return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()"; case CAIRO_STATUS_NO_CURRENT_POINT: return "no current point defined"; case CAIRO_STATUS_INVALID_MATRIX: @@ -118,12 +118,17 @@ cairo_status_to_string (cairo_status_t status) case CAIRO_STATUS_INVALID_CLUSTERS: return "input clusters do not represent the accompanying text and glyph arrays"; case CAIRO_STATUS_INVALID_SLANT: - return "invalid value for an input #cairo_font_slant_t"; + return "invalid value for an input cairo_font_slant_t"; case CAIRO_STATUS_INVALID_WEIGHT: - return "input value for an input #cairo_font_weight_t"; + return "invalid value for an input cairo_font_weight_t"; + case CAIRO_STATUS_INVALID_SIZE: + return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)"; + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + return "user-font method not implemented"; + default: + case CAIRO_STATUS_LAST_STATUS: + return "<unknown error status>"; } - - return "<unknown error status>"; } @@ -287,8 +292,8 @@ _cairo_validate_text_clusters (const char *utf8, /* Make sure we've got valid UTF-8 for the cluster */ status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL); - if (status) - return CAIRO_STATUS_INVALID_CLUSTERS; + if (unlikely (status)) + return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); n_bytes += cluster_bytes ; n_glyphs += cluster_glyphs; @@ -296,7 +301,7 @@ _cairo_validate_text_clusters (const char *utf8, if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) { BAD: - return CAIRO_STATUS_INVALID_CLUSTERS; + return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); } return CAIRO_STATUS_SUCCESS; @@ -382,17 +387,8 @@ _cairo_operator_bounded_by_source (cairo_operator_t op) } -void -_cairo_restrict_value (double *value, double min, double max) -{ - if (*value < min) - *value = min; - else if (*value > max) - *value = max; -} - /* This function is identical to the C99 function lround(), except that it - * performs arithmetic rounding (instead of away-from-zero rounding) and + * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and * has a valid input range of (INT_MIN, INT_MAX] instead of * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems * than other commonly used methods for rounding (lround, round, rint, lrint @@ -617,10 +613,12 @@ _cairo_lround (double d) #include <windows.h> #include <io.h> -/* tmpfile() replacment for Windows. +#if !_WIN32_WCE +/* tmpfile() replacement for Windows. * * On Windows tmpfile() creates the file in the root directory. This - * may fail due to unsufficient privileges. + * may fail due to unsufficient privileges. However, this isn't a + * problem on Windows CE so we don't use it there. */ FILE * _cairo_win32_tmpfile (void) @@ -665,5 +663,114 @@ _cairo_win32_tmpfile (void) return fp; } +#endif /* !_WIN32_WCE */ #endif /* _WIN32 */ + +typedef struct _cairo_intern_string { + cairo_hash_entry_t hash_entry; + int len; + char *string; +} cairo_intern_string_t; + +static cairo_hash_table_t *_cairo_intern_string_ht; + +static unsigned long +_intern_string_hash (const char *str, int len) +{ + const signed char *p = (const signed char *) str; + unsigned int h = *p; + + for (p += 1; --len; p++) + h = (h << 5) - h + *p; + + return h; +} + +static cairo_bool_t +_intern_string_equal (const void *_a, const void *_b) +{ + const cairo_intern_string_t *a = _a; + const cairo_intern_string_t *b = _b; + + if (a->len != b->len) + return FALSE; + + return memcmp (a->string, b->string, a->len) == 0; +} + +cairo_status_t +_cairo_intern_string (const char **str_inout, int len) +{ + char *str = (char *) *str_inout; + cairo_intern_string_t tmpl, *istring; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (len < 0) + len = strlen (str); + tmpl.hash_entry.hash = _intern_string_hash (str, len); + tmpl.len = len; + tmpl.string = (char *) str; + + CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex); + if (_cairo_intern_string_ht == NULL) { + _cairo_intern_string_ht = _cairo_hash_table_create (_intern_string_equal); + if (unlikely (_cairo_intern_string_ht == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + istring = _cairo_hash_table_lookup (_cairo_intern_string_ht, + &tmpl.hash_entry); + if (istring == NULL) { + istring = malloc (sizeof (cairo_intern_string_t) + len + 1); + if (likely (istring != NULL)) { + istring->hash_entry.hash = tmpl.hash_entry.hash; + istring->len = tmpl.len; + istring->string = (char *) (istring + 1); + memcpy (istring->string, str, len); + istring->string[len] = '\0'; + + status = _cairo_hash_table_insert (_cairo_intern_string_ht, + &istring->hash_entry); + if (unlikely (status)) { + free (istring); + goto BAIL; + } + } else { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + *str_inout = istring->string; + + BAIL: + CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex); + return status; +} + +static void +_intern_string_pluck (void *entry, void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + +void +_cairo_intern_string_reset_static_data (void) +{ + CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex); + if (_cairo_intern_string_ht != NULL) { + _cairo_hash_table_foreach (_cairo_intern_string_ht, + _intern_string_pluck, + _cairo_intern_string_ht); + _cairo_hash_table_destroy(_cairo_intern_string_ht); + _cairo_intern_string_ht = NULL; + } + CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex); +} diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h index 239dbbb..a956b52 100644 --- a/src/cairo-mutex-impl-private.h +++ b/src/cairo-mutex-impl-private.h @@ -53,9 +53,10 @@ /* A fully qualified no-operation statement */ #define CAIRO_MUTEX_IMPL_NOOP do {/*no-op*/} while (0) -/* And one that evaluates it's argument once */ -#define CAIRO_MUTEX_IMPL_NOOP1(expr) do { if (expr) ; } while (0) - +/* And one that evaluates its argument once */ +#define CAIRO_MUTEX_IMPL_NOOP1(expr) do { (void)(expr); } while (0) +/* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the + * result of __attribute__((warn_used_result)) functions. */ /* Cairo mutex implementation: * diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h index 6fdb389..a807d7a 100644 --- a/src/cairo-mutex-list-private.h +++ b/src/cairo-mutex-list-private.h @@ -36,11 +36,12 @@ #define CAIRO_MUTEX_DECLARE(mutex) #endif -CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_pattern_cache_lock) CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) -CAIRO_MUTEX_DECLARE (_cairo_font_face_mutex) +CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex) +CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex) +CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex) #if CAIRO_HAS_FT_FONT diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c index e4cef05..82bab3b 100644 --- a/src/cairo-os2-surface.c +++ b/src/cairo-os2-surface.c @@ -39,7 +39,9 @@ #include "cairo-os2-private.h" +#if CAIRO_HAS_FC_FONT #include <fontconfig/fontconfig.h> +#endif #include <float.h> #ifdef BUILD_CAIRO_DLL @@ -101,7 +103,7 @@ cairo_os2_init (void) DisableFPUException (); -#if CAIRO_HAS_FT_FONT +#if CAIRO_HAS_FC_FONT /* Initialize FontConfig */ FcInit (); #endif @@ -130,16 +132,9 @@ cairo_os2_fini (void) DisableFPUException (); - /* Free allocated memories! */ - /* (Check cairo_debug_reset_static_data () for an example of this!) */ - _cairo_font_face_reset_static_data (); -#if CAIRO_HAS_FT_FONT - _cairo_ft_font_reset_static_data (); -#endif - - CAIRO_MUTEX_FINALIZE (); + cairo_debug_reset_static_data (); -#if CAIRO_HAS_FT_FONT +#if CAIRO_HAS_FC_FONT # if HAVE_FCFINI /* Uninitialize FontConfig */ FcFini (); @@ -777,7 +772,7 @@ cairo_os2_surface_create (HPS hps_client_window, (height <= 0)) { /* Invalid window size! */ - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); @@ -795,6 +790,7 @@ cairo_os2_surface_create (HPS hps_client_window, FALSE); if (rc != NO_ERROR) { /* Could not create mutex semaphore! */ + free (local_os2_surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } @@ -881,8 +877,9 @@ cairo_os2_surface_create (HPS hps_client_window, * * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, - * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, for invalid - * sizes, or if the timeout happened before all the buffers were released + * %CAIRO_STATUS_INVALID_SIZE for invalid sizes + * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the + * timeout happened before all the buffers were released * * Since: 1.4 **/ @@ -909,7 +906,7 @@ cairo_os2_surface_set_size (cairo_surface_t *surface, (new_height <= 0)) { /* Invalid size! */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); } /* Allocate memory for new stuffs */ @@ -1326,6 +1323,8 @@ static const cairo_surface_backend_t cairo_os2_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h index 787d061..2b3d584 100644 --- a/src/cairo-output-stream-private.h +++ b/src/cairo-output-stream-private.h @@ -43,14 +43,20 @@ #include <stdio.h> #include <stdarg.h> -typedef cairo_status_t (*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, - const unsigned char *data, - unsigned int length); +typedef cairo_status_t +(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, + const unsigned char *data, + unsigned int length); -typedef cairo_status_t (*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); +typedef cairo_status_t +(*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream); + +typedef cairo_status_t +(*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); struct _cairo_output_stream { cairo_output_stream_write_func_t write_func; + cairo_output_stream_flush_func_t flush_func; cairo_output_stream_close_func_t close_func; unsigned long position; cairo_status_t status; @@ -62,6 +68,7 @@ extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil; cairo_private void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, cairo_output_stream_close_func_t close_func); cairo_private cairo_status_t @@ -93,6 +100,10 @@ _cairo_output_stream_create (cairo_write_func_t write_func, cairo_private cairo_output_stream_t * _cairo_output_stream_create_in_error (cairo_status_t status); +/* Tries to flush any buffer maintained by the stream or its delegates. */ +cairo_private cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream); + /* Returns the final status value associated with this object, just * before its last gasp. This final status value will capture any * status failure returned by the stream's close_func as well. */ @@ -161,6 +172,11 @@ _cairo_memory_stream_copy (cairo_output_stream_t *base, cairo_private int _cairo_memory_stream_length (cairo_output_stream_t *stream); +cairo_private cairo_status_t +_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, + unsigned char **data_out, + unsigned int *length_out); + cairo_private cairo_output_stream_t * _cairo_null_stream_create (void); diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index bae6ac4..7115433 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -70,9 +70,11 @@ void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, cairo_output_stream_close_func_t close_func) { stream->write_func = write_func; + stream->flush_func = flush_func; stream->close_func = close_func; stream->position = 0; stream->status = CAIRO_STATUS_SUCCESS; @@ -87,6 +89,7 @@ _cairo_output_stream_fini (cairo_output_stream_t *stream) const cairo_output_stream_t _cairo_output_stream_nil = { NULL, /* write_func */ + NULL, /* flush_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_NO_MEMORY, @@ -95,6 +98,7 @@ const cairo_output_stream_t _cairo_output_stream_nil = { static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { NULL, /* write_func */ + NULL, /* flush_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_WRITE_ERROR, @@ -143,12 +147,13 @@ _cairo_output_stream_create (cairo_write_func_t write_func, cairo_output_stream_with_closure_t *stream; stream = malloc (sizeof (cairo_output_stream_with_closure_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, closure_write, closure_close); + _cairo_output_stream_init (&stream->base, + closure_write, NULL, closure_close); stream->write_func = write_func; stream->close_func = close_func; stream->closure = closure; @@ -168,18 +173,42 @@ _cairo_output_stream_create_in_error (cairo_status_t status) return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; stream = malloc (sizeof (cairo_output_stream_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (stream, NULL, NULL); + _cairo_output_stream_init (stream, NULL, NULL, NULL); stream->status = status; return stream; } cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream) +{ + cairo_status_t status; + + if (stream->closed) + return stream->status; + + if (stream == &_cairo_output_stream_nil || + stream == &_cairo_output_stream_nil_write_error) + { + return stream->status; + } + + if (stream->flush_func) { + status = stream->flush_func (stream); + /* Don't overwrite a pre-existing status failure. */ + if (stream->status == CAIRO_STATUS_SUCCESS) + stream->status = status; + } + + return stream->status; +} + +cairo_status_t _cairo_output_stream_close (cairo_output_stream_t *stream) { cairo_status_t status; @@ -569,12 +598,13 @@ _cairo_output_stream_create_for_file (FILE *file) } stream = malloc (sizeof *stream); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, stdio_write, stdio_flush); + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_flush); stream->file = file; return &stream->base; @@ -602,13 +632,14 @@ _cairo_output_stream_create_for_filename (const char *filename) } stream = malloc (sizeof *stream); - if (stream == NULL) { + if (unlikely (stream == NULL)) { fclose (file); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, stdio_write, stdio_close); + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_close); stream->file = file; return &stream->base; @@ -645,17 +676,43 @@ _cairo_memory_stream_create (void) memory_stream_t *stream; stream = malloc (sizeof *stream); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, memory_write, memory_close); + _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close); _cairo_array_init (&stream->array, 1); return &stream->base; } +cairo_status_t +_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, + unsigned char **data_out, + unsigned int *length_out) +{ + memory_stream_t *stream; + cairo_status_t status; + + status = abstract_stream->status; + if (unlikely (status)) + return _cairo_output_stream_destroy (abstract_stream); + + stream = (memory_stream_t *) abstract_stream; + + *length_out = _cairo_array_num_elements (&stream->array); + *data_out = malloc (*length_out); + if (unlikely (*data_out == NULL)) { + status = _cairo_output_stream_destroy (abstract_stream); + assert (status == CAIRO_STATUS_SUCCESS); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out); + + return _cairo_output_stream_destroy (abstract_stream); +} + void _cairo_memory_stream_copy (cairo_output_stream_t *base, cairo_output_stream_t *dest) @@ -670,7 +727,7 @@ _cairo_memory_stream_copy (cairo_output_stream_t *base, return; } - _cairo_output_stream_write (dest, + _cairo_output_stream_write (dest, _cairo_array_index (&stream->array, 0), _cairo_array_num_elements (&stream->array)); } @@ -696,12 +753,12 @@ _cairo_null_stream_create (void) cairo_output_stream_t *stream; stream = malloc (sizeof *stream); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (stream, null_write, NULL); + _cairo_output_stream_init (stream, null_write, NULL, NULL); return stream; } diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index a94ca80..b84fbff 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -77,7 +77,7 @@ _cairo_paginated_surface_create (cairo_surface_t *target, cairo_status_t status; surface = malloc (sizeof (cairo_paginated_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } @@ -99,7 +99,7 @@ _cairo_paginated_surface_create (cairo_surface_t *target, surface->meta = _cairo_meta_surface_create (content, width, height); status = cairo_surface_status (surface->meta); - if (status) + if (unlikely (status)) goto FAIL_CLEANUP_SURFACE; surface->page_num = 1; @@ -151,7 +151,7 @@ _cairo_paginated_surface_set_size (cairo_surface_t *surface, paginated_surface->meta = _cairo_meta_surface_create (paginated_surface->content, width, height); status = cairo_surface_status (paginated_surface->meta); - if (status) + if (unlikely (status)) return _cairo_surface_set_error (surface, status); return CAIRO_STATUS_SUCCESS; @@ -215,7 +215,7 @@ _cairo_paginated_surface_acquire_source_image (void *abstract_surface, cairo_rectangle_int_t extents; status = _cairo_surface_get_extents (surface->target, &extents); - if (status) + if (unlikely (status)) return status; image = _cairo_paginated_surface_create_image_surface (surface, @@ -223,7 +223,7 @@ _cairo_paginated_surface_acquire_source_image (void *abstract_surface, extents.height); status = _cairo_meta_surface_replay (surface->meta, image); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (image); return status; } @@ -244,7 +244,7 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface, static cairo_int_status_t _paint_fallback_image (cairo_paginated_surface_t *surface, - cairo_box_int_t *box) + cairo_rectangle_int_t *rect) { double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; @@ -252,12 +252,12 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, int x, y, width, height; cairo_status_t status; cairo_surface_t *image; - cairo_pattern_t *pattern; + cairo_surface_pattern_t pattern; - x = box->p1.x; - y = box->p1.y; - width = box->p2.x - x; - height = box->p2.y - y; + x = rect->x; + y = rect->y; + width = rect->width; + height = rect->height; image = _cairo_paginated_surface_create_image_surface (surface, ceil (width * x_scale), ceil (height * y_scale)); @@ -267,18 +267,21 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); status = _cairo_meta_surface_replay (surface->meta, image); - if (status) + if (unlikely (status)) goto CLEANUP_IMAGE; - pattern = cairo_pattern_create_for_surface (image); + _cairo_pattern_init_for_surface (&pattern, image); cairo_matrix_init (&matrix, x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); - cairo_pattern_set_matrix (pattern, &matrix); + cairo_pattern_set_matrix (&pattern.base, &matrix); + /* the fallback should be rendered at native resolution, so disable + * filtering (if possible) to avoid introducing potential artifacts. */ + pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, - pattern); + &pattern.base, NULL); - cairo_pattern_destroy (pattern); + _cairo_pattern_fini (&pattern.base); CLEANUP_IMAGE: cairo_surface_destroy (image); @@ -315,7 +318,7 @@ _paint_page (cairo_paginated_surface_t *surface) _cairo_analysis_surface_get_bounding_box (analysis, &bbox); status = surface->backend->set_bounding_box (surface->target, &bbox); - if (status) + if (unlikely (status)) goto FAIL; } @@ -324,7 +327,7 @@ _paint_page (cairo_paginated_surface_t *surface) status = surface->backend->set_fallback_images_required (surface->target, has_fallbacks); - if (status) + if (unlikely (status)) goto FAIL; } @@ -357,29 +360,28 @@ _paint_page (cairo_paginated_surface_t *surface) surface->target, CAIRO_META_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (status) + if (unlikely (status)) goto FAIL; } if (has_page_fallback) { - cairo_box_int_t box; + cairo_rectangle_int_t rect; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); - box.p1.x = 0; - box.p1.y = 0; - box.p2.x = surface->width; - box.p2.y = surface->height; - status = _paint_fallback_image (surface, &box); - if (status) + rect.x = 0; + rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + status = _paint_fallback_image (surface, &rect); + if (unlikely (status)) goto FAIL; } if (has_finegrained_fallback) { cairo_region_t *region; - cairo_box_int_t *boxes; - int num_boxes, i; + int num_rects, i; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); @@ -390,21 +392,22 @@ _paint_page (cairo_paginated_surface_t *surface) CAIRO_FILL_RULE_WINDING, CAIRO_GSTATE_TOLERANCE_DEFAULT, CAIRO_ANTIALIAS_DEFAULT); - if (status) + if (unlikely (status)) goto FAIL; region = _cairo_analysis_surface_get_unsupported (analysis); - status = _cairo_region_get_boxes (region, &num_boxes, &boxes); - if (status) - goto FAIL; - for (i = 0; i < num_boxes; i++) { - status = _paint_fallback_image (surface, &boxes[i]); - if (status) { - _cairo_region_boxes_fini (region, boxes); + + num_rects = cairo_region_num_rectangles (region); + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + status = _paint_fallback_image (surface, &rect); + + if (unlikely (status)) goto FAIL; - } } - _cairo_region_boxes_fini (region, boxes); } FAIL: @@ -433,11 +436,11 @@ _cairo_paginated_surface_copy_page (void *abstract_surface) cairo_paginated_surface_t *surface = abstract_surface; status = _start_page (surface); - if (status) + if (unlikely (status)) return status; status = _paint_page (surface); - if (status) + if (unlikely (status)) return status; surface->page_num++; @@ -460,20 +463,20 @@ _cairo_paginated_surface_show_page (void *abstract_surface) cairo_paginated_surface_t *surface = abstract_surface; status = _start_page (surface); - if (status) + if (unlikely (status)) return status; status = _paint_page (surface); - if (status) + if (unlikely (status)) return status; cairo_surface_show_page (surface->target); status = cairo_surface_status (surface->target); - if (status) + if (unlikely (status)) return status; status = cairo_surface_status (surface->meta); - if (status) + if (unlikely (status)) return status; cairo_surface_destroy (surface->meta); @@ -482,7 +485,7 @@ _cairo_paginated_surface_show_page (void *abstract_surface) surface->width, surface->height); status = cairo_surface_status (surface->meta); - if (status) + if (unlikely (status)) return status; surface->page_num++; @@ -526,7 +529,8 @@ _cairo_paginated_surface_get_font_options (void *abstract_surfa static cairo_int_status_t _cairo_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_paginated_surface_t *surface = abstract_surface; @@ -536,30 +540,32 @@ _cairo_paginated_surface_paint (void *abstract_surface, surface->page_is_blank = FALSE; - return _cairo_surface_paint (surface->meta, op, source); + return _cairo_surface_paint (surface->meta, op, source, NULL); } static cairo_int_status_t _cairo_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { cairo_paginated_surface_t *surface = abstract_surface; - return _cairo_surface_mask (surface->meta, op, source, mask); + return _cairo_surface_mask (surface->meta, op, source, mask, NULL); } static cairo_int_status_t _cairo_paginated_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_paginated_surface_t *surface = abstract_surface; @@ -572,17 +578,18 @@ _cairo_paginated_surface_stroke (void *abstract_surface, return _cairo_surface_stroke (surface->meta, op, source, path, style, ctm, ctm_inverse, - tolerance, antialias); + tolerance, antialias, NULL); } static cairo_int_status_t _cairo_paginated_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_paginated_surface_t *surface = abstract_surface; @@ -594,7 +601,7 @@ _cairo_paginated_surface_fill (void *abstract_surface, return _cairo_surface_fill (surface->meta, op, source, path, fill_rule, - tolerance, antialias); + tolerance, antialias, NULL); } static cairo_bool_t @@ -608,7 +615,7 @@ _cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface) static cairo_int_status_t _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -616,7 +623,8 @@ _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) { cairo_paginated_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -642,7 +650,7 @@ _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - scaled_font); + scaled_font, NULL); return status; } @@ -667,6 +675,8 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ _cairo_paginated_surface_copy_page, _cairo_paginated_surface_show_page, NULL, /* set_clip_region */ @@ -688,6 +698,7 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ _cairo_paginated_surface_has_show_text_glyphs, _cairo_paginated_surface_show_text_glyphs }; diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c index 70867e3..1fadd84 100644 --- a/src/cairo-path-bounds.c +++ b/src/cairo-path-bounds.c @@ -37,96 +37,127 @@ #include "cairoint.h" typedef struct cairo_path_bounder { - cairo_point_t move_to_point; - cairo_bool_t has_move_to_point; + cairo_point_t current_point; + cairo_bool_t has_initial_point; cairo_bool_t has_point; - cairo_fixed_t min_x; - cairo_fixed_t min_y; - cairo_fixed_t max_x; - cairo_fixed_t max_y; + cairo_box_t extents; } cairo_path_bounder_t; static void -_cairo_path_bounder_init (cairo_path_bounder_t *bounder); - -static void -_cairo_path_bounder_fini (cairo_path_bounder_t *bounder); - -static void -_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *point); - -static cairo_status_t -_cairo_path_bounder_move_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_path_bounder_line_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_path_bounder_close_path (void *closure); - -static void _cairo_path_bounder_init (cairo_path_bounder_t *bounder) { - bounder->has_move_to_point = FALSE; + bounder->has_initial_point = FALSE; bounder->has_point = FALSE; } static void _cairo_path_bounder_fini (cairo_path_bounder_t *bounder) { - bounder->has_move_to_point = FALSE; + bounder->has_initial_point = FALSE; bounder->has_point = FALSE; } static void -_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *point) +_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, + const cairo_point_t *point) { if (bounder->has_point) { - if (point->x < bounder->min_x) - bounder->min_x = point->x; + if (point->x < bounder->extents.p1.x) + bounder->extents.p1.x = point->x; - if (point->y < bounder->min_y) - bounder->min_y = point->y; + if (point->y < bounder->extents.p1.y) + bounder->extents.p1.y = point->y; - if (point->x > bounder->max_x) - bounder->max_x = point->x; + if (point->x > bounder->extents.p2.x) + bounder->extents.p2.x = point->x; - if (point->y > bounder->max_y) - bounder->max_y = point->y; + if (point->y > bounder->extents.p2.y) + bounder->extents.p2.y = point->y; } else { - bounder->min_x = point->x; - bounder->min_y = point->y; - bounder->max_x = point->x; - bounder->max_y = point->y; + bounder->extents.p1.x = point->x; + bounder->extents.p1.y = point->y; + bounder->extents.p2.x = point->x; + bounder->extents.p2.y = point->y; bounder->has_point = TRUE; } } static cairo_status_t -_cairo_path_bounder_move_to (void *closure, cairo_point_t *point) +_cairo_path_bounder_move_to (void *closure, + const cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; - bounder->move_to_point = *point; - bounder->has_move_to_point = TRUE; + bounder->current_point = *point; + bounder->has_initial_point = TRUE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_line_to (void *closure, cairo_point_t *point) +_cairo_path_bounder_line_to (void *closure, + const cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; - if (bounder->has_move_to_point) { - _cairo_path_bounder_add_point (bounder, - &bounder->move_to_point); - bounder->has_move_to_point = FALSE; + if (bounder->has_initial_point) { + _cairo_path_bounder_add_point (bounder, &bounder->current_point); + bounder->has_initial_point = FALSE; } _cairo_path_bounder_add_point (bounder, point); + bounder->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_bounder_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_path_bounder_t *bounder = closure; + + /* If the bbox of the control points is entirely inside, then we + * do not need to further evaluate the spline. + */ + if (! bounder->has_point || + b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x || + b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y || + c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x || + c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y || + d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x || + d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y) + { + return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder, + &bounder->current_point, b, c, d); + } + else + { + /* All control points are within the current extents. */ + return CAIRO_STATUS_SUCCESS; + } +} + +static cairo_status_t +_cairo_path_bounder_curve_to_cp (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_path_bounder_t *bounder = closure; + + if (bounder->has_initial_point) { + _cairo_path_bounder_add_point (bounder, &bounder->current_point); + bounder->has_initial_point = FALSE; + } + + _cairo_path_bounder_add_point (bounder, b); + _cairo_path_bounder_add_point (bounder, c); + _cairo_path_bounder_add_point (bounder, d); return CAIRO_STATUS_SUCCESS; } @@ -137,30 +168,129 @@ _cairo_path_bounder_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -/* XXX: Perhaps this should compute a PixRegion rather than 4 doubles */ -cairo_status_t +/* This computes the extents of all the points in the path, not those of + * the damage area (i.e it does not consider winding and it only inspects + * the control points of the curves, not the flattened path). + */ +void +_cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to_cp, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + + if (bounder.has_point) { + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } + + _cairo_path_bounder_fini (&bounder); +} + +/* A slightly better approximation than above - we actually decompose the + * Bezier, but we continue to ignore winding. + */ +void +_cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + + if (bounder.has_point) { + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } + + _cairo_path_bounder_fini (&bounder); +} + +/* Adjusts the fill extents (above) by the device-space pen. */ +void +_cairo_path_fixed_approximate_stroke_extents (cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + cairo_rectangle_int_t *extents) +{ + cairo_path_bounder_t bounder; + cairo_status_t status; + + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + + if (bounder.has_point) { + double dx, dy; + + _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); + + bounder.extents.p1.x -= _cairo_fixed_from_double (dx); + bounder.extents.p2.x += _cairo_fixed_from_double (dx); + bounder.extents.p1.y -= _cairo_fixed_from_double (dy); + bounder.extents.p2.y += _cairo_fixed_from_double (dy); + + _cairo_box_round_to_rectangle (&bounder.extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } + + _cairo_path_bounder_fini (&bounder); +} + +void _cairo_path_fixed_bounds (cairo_path_fixed_t *path, double *x1, double *y1, - double *x2, double *y2, - double tolerance) + double *x2, double *y2) { cairo_path_bounder_t bounder; cairo_status_t status; _cairo_path_bounder_init (&bounder); - status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_close_path, - &bounder, - tolerance); - - if (status == CAIRO_STATUS_SUCCESS && bounder.has_point) { - *x1 = _cairo_fixed_to_double (bounder.min_x); - *y1 = _cairo_fixed_to_double (bounder.min_y); - *x2 = _cairo_fixed_to_double (bounder.max_x); - *y2 = _cairo_fixed_to_double (bounder.max_y); + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + + if (bounder.has_point) { + *x1 = _cairo_fixed_to_double (bounder.extents.p1.x); + *y1 = _cairo_fixed_to_double (bounder.extents.p1.y); + *x2 = _cairo_fixed_to_double (bounder.extents.p2.x); + *y2 = _cairo_fixed_to_double (bounder.extents.p2.y); } else { *x1 = 0.0; *y1 = 0.0; @@ -169,6 +299,4 @@ _cairo_path_fixed_bounds (cairo_path_fixed_t *path, } _cairo_path_bounder_fini (&bounder); - - return status; } diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index 1cef20e..9569c9e 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -47,27 +47,6 @@ typedef struct cairo_filler { } cairo_filler_t; static void -_cairo_filler_init (cairo_filler_t *filler, double tolerance, cairo_traps_t *traps); - -static void -_cairo_filler_fini (cairo_filler_t *filler); - -static cairo_status_t -_cairo_filler_move_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_filler_line_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_filler_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -static cairo_status_t -_cairo_filler_close_path (void *closure); - -static void _cairo_filler_init (cairo_filler_t *filler, double tolerance, cairo_traps_t *traps) { filler->tolerance = tolerance; @@ -86,7 +65,8 @@ _cairo_filler_fini (cairo_filler_t *filler) } static cairo_status_t -_cairo_filler_move_to (void *closure, cairo_point_t *point) +_cairo_filler_move_to (void *closure, + const cairo_point_t *point) { cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; @@ -100,7 +80,8 @@ _cairo_filler_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cairo_filler_line_to (void *closure, cairo_point_t *point) +_cairo_filler_line_to (void *closure, + const cairo_point_t *point) { cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; @@ -114,34 +95,22 @@ _cairo_filler_line_to (void *closure, cairo_point_t *point) static cairo_status_t _cairo_filler_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) { - int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = &filler->polygon; cairo_spline_t spline; - status = _cairo_spline_init (&spline, &filler->current_point, b, c, d); - - if (status == CAIRO_INT_STATUS_DEGENERATE) + if (! _cairo_spline_init (&spline, + _cairo_filler_line_to, + filler, + &filler->current_point, b, c, d)) + { return CAIRO_STATUS_SUCCESS; + } - status = _cairo_spline_decompose (&spline, filler->tolerance); - if (status) - goto CLEANUP_SPLINE; - - for (i = 1; i < spline.num_points; i++) - _cairo_polygon_line_to (polygon, &spline.points[i]); - - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); - - filler->current_point = *d; - - return status; + return _cairo_spline_decompose (&spline, filler->tolerance); } static cairo_status_t @@ -157,6 +126,7 @@ _cairo_filler_close_path (void *closure) static cairo_int_status_t _cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, cairo_traps_t *traps); cairo_status_t @@ -170,7 +140,7 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, /* Before we do anything else, we use a special-case filler for * a device-axis aligned rectangle if possible. */ - status = _cairo_path_fixed_fill_rectangle (path, traps); + status = _cairo_path_fixed_fill_rectangle (path, fill_rule, traps); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; @@ -183,18 +153,18 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, _cairo_filler_curve_to, _cairo_filler_close_path, &filler); - if (status) + if (unlikely (status)) goto BAIL; _cairo_polygon_close (&filler.polygon); status = _cairo_polygon_status (&filler.polygon); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_bentley_ottmann_tessellate_polygon (filler.traps, &filler.polygon, fill_rule); - if (status) + if (unlikely (status)) goto BAIL; BAIL: @@ -212,27 +182,77 @@ BAIL: */ static cairo_int_status_t _cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, cairo_traps_t *traps) { - if (_cairo_path_fixed_is_box (path, NULL)) { - cairo_point_t *p = path->buf_head.base.points; - cairo_point_t *top_left, *bot_right; - - top_left = &p[0]; - bot_right = &p[2]; - if (top_left->x > bot_right->x || top_left->y > bot_right->y) { - int n; - - /* not a simple cairo_rectangle() */ - for (n = 0; n < 4; n++) { - if (p[n].x <= top_left->x && p[n].y <= top_left->y) - top_left = &p[n]; - if (p[n].x >= bot_right->x && p[n].y >= bot_right->y) - bot_right = &p[n]; + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + if (box.p1.x > box.p2.x) { + cairo_fixed_t t; + + t = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = t; + } + + if (box.p1.y > box.p2.y) { + cairo_fixed_t t; + + t = box.p1.y; + box.p1.y = box.p2.y; + box.p2.y = t; + } + + return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); + } else if (fill_rule == CAIRO_FILL_RULE_WINDING) { + cairo_path_fixed_iter_t iter; + int last_cw = -1; + + /* Support a series of rectangles as can be expected to describe a + * GdkRegion clip region during exposes. + */ + _cairo_path_fixed_iter_init (&iter, path); + while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { + cairo_status_t status; + int cw = 0; + + if (box.p1.x > box.p2.x) { + cairo_fixed_t t; + + t = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = t; + + cw = ! cw; + } + + if (box.p1.y > box.p2.y) { + cairo_fixed_t t; + + t = box.p1.y; + box.p1.y = box.p2.y; + box.p2.y = t; + + cw = ! cw; } + + if (last_cw < 0) { + last_cw = cw; + } else if (last_cw != cw) { + _cairo_traps_clear (traps); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_traps_tessellate_rectangle (traps, + &box.p1, &box.p2); + if (unlikely (status)) + return status; } + if (_cairo_path_fixed_iter_at_end (&iter)) + return CAIRO_STATUS_SUCCESS; - return _cairo_traps_tessellate_rectangle (traps, top_left, bot_right); + _cairo_traps_clear (traps); } return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h index 4a5990d..0ade988 100644 --- a/src/cairo-path-fixed-private.h +++ b/src/cairo-path-fixed-private.h @@ -37,6 +37,7 @@ #define CAIRO_PATH_FIXED_PRIVATE_H #include "cairo-types-private.h" +#include "cairo-compiler-private.h" enum cairo_path_op { CAIRO_PATH_OP_MOVE_TO = 0, @@ -53,9 +54,9 @@ typedef char cairo_path_op_t; typedef struct _cairo_path_buf { struct _cairo_path_buf *next, *prev; - int buf_size; - int num_ops; - int num_points; + unsigned int buf_size; + unsigned int num_ops; + unsigned int num_points; cairo_path_op_t *op; cairo_point_t *points; @@ -77,4 +78,31 @@ struct _cairo_path_fixed { cairo_path_buf_fixed_t buf_head; }; +cairo_private unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path); + +cairo_private unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b); + +typedef struct _cairo_path_fixed_iter { + cairo_path_buf_t *buf; + unsigned int n_op; + unsigned int n_point; +} cairo_path_fixed_iter_t; + +cairo_private void +_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, + cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, + cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); + #endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c index a915ada..53d6cd4 100644 --- a/src/cairo-path-fixed.c +++ b/src/cairo-path-fixed.c @@ -69,6 +69,8 @@ _cairo_path_buf_add_points (cairo_path_buf_t *buf, void _cairo_path_fixed_init (cairo_path_fixed_t *path) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); + path->buf_head.base.next = NULL; path->buf_head.base.prev = NULL; path->buf_tail = &path->buf_head.base; @@ -120,7 +122,7 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, buf_size = MAX (num_ops, (num_points + 1) / 2); if (buf_size) { buf = _cairo_path_buf_create (buf_size); - if (buf == NULL) { + if (unlikely (buf == NULL)) { _cairo_path_fixed_fini (path); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -144,6 +146,170 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, return CAIRO_STATUS_SUCCESS; } +unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path) +{ + unsigned long hash = 0; + const cairo_path_buf_t *buf; + int num_points, num_ops; + + hash = _cairo_hash_bytes (hash, + &path->current_point, + sizeof (path->current_point)); + hash = _cairo_hash_bytes (hash, + &path->last_move_point, + sizeof (path->last_move_point)); + + num_ops = path->buf_head.base.num_ops; + num_points = path->buf_head.base.num_points; + for (buf = path->buf_head.base.next; + buf != NULL; + buf = buf->next) + { + hash = _cairo_hash_bytes (hash, buf->op, + buf->num_ops * sizeof (buf->op[0])); + hash = _cairo_hash_bytes (hash, buf->points, + buf->num_points * sizeof (buf->points[0])); + + num_ops += buf->num_ops; + num_points += buf->num_points; + } + + hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops)); + hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points)); + + return hash; +} + +unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path) +{ + const cairo_path_buf_t *buf; + int num_points, num_ops; + + num_ops = path->buf_head.base.num_ops; + num_points = path->buf_head.base.num_points; + for (buf = path->buf_head.base.next; + buf != NULL; + buf = buf->next) + { + num_ops += buf->num_ops; + num_points += buf->num_points; + } + + return num_ops * sizeof (buf->op[0]) + + num_points * sizeof (buf->points[0]); +} + +cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b) +{ + const cairo_path_buf_t *buf_a, *buf_b; + const cairo_path_op_t *ops_a, *ops_b; + const cairo_point_t *points_a, *points_b; + int num_points_a, num_ops_a; + int num_points_b, num_ops_b; + + if (a == b) + return TRUE; + + if (a != NULL) { + num_ops_a = a->buf_head.base.num_ops; + num_points_a = a->buf_head.base.num_points; + for (buf_a = a->buf_head.base.next; + buf_a != NULL; + buf_a = buf_a->next) + { + num_ops_a += buf_a->num_ops; + num_points_a += buf_a->num_points; + } + } else + num_ops_a = num_points_a = 0; + + if (b != NULL) { + num_ops_b = b->buf_head.base.num_ops; + num_points_b = b->buf_head.base.num_points; + for (buf_b = b->buf_head.base.next; + buf_b != NULL; + buf_b = buf_b->next) + { + num_ops_b += buf_b->num_ops; + num_points_b += buf_b->num_points; + } + } else + num_ops_b = num_points_b = 0; + + if (num_ops_a == 0 && num_ops_b == 0) + return TRUE; + + if (num_ops_a != num_ops_b || num_points_a != num_points_b) + return FALSE; + + assert (a != NULL && b != NULL); + + buf_a = &a->buf_head.base; + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + + buf_b = &b->buf_head.base; + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + + while (TRUE) { + int num_ops = MIN (num_ops_a, num_ops_b); + int num_points = MIN (num_points_a, num_points_b); + + if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t))) + return FALSE; + if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t))) + return FALSE; + + num_ops_a -= num_ops; + ops_a += num_ops; + num_points_a -= num_points; + points_a += num_points; + if (num_ops_a == 0 || num_points_a == 0) { + if (num_ops_a || num_points_a) + return FALSE; + + buf_a = buf_a->next; + if (buf_a == NULL) + break; + + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + } + + num_ops_b -= num_ops; + ops_b += num_ops; + num_points_b -= num_points; + points_b += num_points; + if (num_ops_b == 0 || num_points_b == 0) { + if (num_ops_b || num_points_b) + return FALSE; + + buf_b = buf_b->next; + if (buf_b == NULL) + break; + + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + } + } + + return TRUE; +} + + cairo_path_fixed_t * _cairo_path_fixed_create (void) { @@ -170,14 +336,8 @@ _cairo_path_fixed_fini (cairo_path_fixed_t *path) buf = buf->next; _cairo_path_buf_destroy (this); } - path->buf_head.base.next = NULL; - path->buf_head.base.prev = NULL; - path->buf_tail = &path->buf_head.base; - path->buf_head.base.num_ops = 0; - path->buf_head.base.num_points = 0; - path->has_current_point = FALSE; - path->has_curve_to = FALSE; + VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t))); } void @@ -208,7 +368,7 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t *path, *last_move_to_point = point; } else { status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); - if (status) + if (unlikely (status)) return status; } @@ -262,7 +422,7 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path, else status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); - if (status) + if (unlikely (status)) return status; path->current_point = point; @@ -303,12 +463,12 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, if (! path->has_current_point) { status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point[0], 1); - if (status) + if (unlikely (status)) return status; } status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); - if (status) + if (unlikely (status)) return status; path->current_point = point[2]; @@ -355,13 +515,13 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path) return CAIRO_STATUS_SUCCESS; status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); - if (status) + if (unlikely (status)) return status; status = _cairo_path_fixed_move_to (path, path->last_move_point.x, path->last_move_point.y); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -393,7 +553,7 @@ _cairo_path_fixed_add (cairo_path_fixed_t *path, buf->num_points + num_points > 2 * buf->buf_size) { buf = _cairo_path_buf_create (buf->buf_size * 2); - if (buf == NULL) + if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_path_fixed_add_buf (path, buf); @@ -528,7 +688,7 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, status = (*close_path) (closure); break; } - if (status) + if (unlikely (status)) return status; if (forward) { @@ -543,7 +703,7 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, static cairo_status_t _append_move_to (void *closure, - cairo_point_t *point) + const cairo_point_t *point) { cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure; return _cairo_path_fixed_move_to (path, point->x, point->y); @@ -551,7 +711,7 @@ _append_move_to (void *closure, static cairo_status_t _append_line_to (void *closure, - cairo_point_t *point) + const cairo_point_t *point) { cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure; return _cairo_path_fixed_line_to (path, point->x, point->y); @@ -559,9 +719,9 @@ _append_line_to (void *closure, static cairo_status_t _append_curve_to (void *closure, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2) + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) { cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure; return _cairo_path_fixed_curve_to (path, p0->x, p0->y, p1->x, p1->y, p2->x, p2->y); @@ -574,7 +734,7 @@ _append_close_path (void *closure) return _cairo_path_fixed_close_path (path); } -cairo_private cairo_status_t +cairo_status_t _cairo_path_fixed_append (cairo_path_fixed_t *path, const cairo_path_fixed_t *other, cairo_direction_t dir) @@ -595,7 +755,7 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, cairo_fixed_t scaley) { cairo_path_buf_t *buf = &path->buf_head.base; - int i; + unsigned int i; while (buf) { for (i = 0; i < buf->num_points; i++) { @@ -626,7 +786,7 @@ _cairo_path_fixed_transform (cairo_path_fixed_t *path, cairo_matrix_t *matrix) { cairo_path_buf_t *buf; - int i; + unsigned int i; double dx, dy; if (matrix->yx == 0.0 && matrix->xy == 0.0) { @@ -700,7 +860,8 @@ typedef struct cairo_path_flattener { } cpf_t; static cairo_status_t -_cpf_move_to (void *closure, cairo_point_t *point) +_cpf_move_to (void *closure, + const cairo_point_t *point) { cpf_t *cpf = closure; @@ -710,7 +871,8 @@ _cpf_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cpf_line_to (void *closure, cairo_point_t *point) +_cpf_line_to (void *closure, + const cairo_point_t *point) { cpf_t *cpf = closure; @@ -721,38 +883,26 @@ _cpf_line_to (void *closure, cairo_point_t *point) static cairo_status_t _cpf_curve_to (void *closure, - cairo_point_t *p1, - cairo_point_t *p2, - cairo_point_t *p3) + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) { cpf_t *cpf = closure; - cairo_status_t status; cairo_spline_t spline; - int i; cairo_point_t *p0 = &cpf->current_point; - status = _cairo_spline_init (&spline, p0, p1, p2, p3); - if (status == CAIRO_INT_STATUS_DEGENERATE) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_spline_decompose (&spline, cpf->tolerance); - if (status) - goto out; - - for (i=1; i < spline.num_points; i++) { - status = cpf->line_to (cpf->closure, &spline.points[i]); - if (status) - goto out; + if (! _cairo_spline_init (&spline, + cpf->line_to, + cpf->closure, + p0, p1, p2, p3)) + { + return _cpf_line_to (closure, p3); } cpf->current_point = *p3; - status = CAIRO_STATUS_SUCCESS; - - out: - _cairo_spline_fini (&spline); - return status; + return _cairo_spline_decompose (&spline, cpf->tolerance); } static cairo_status_t @@ -775,6 +925,15 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, { cpf_t flattener; + if (!path->has_curve_to) { + return _cairo_path_fixed_interpret (path, dir, + move_to, + line_to, + NULL, + close_path, + closure); + } + flattener.tolerance = tolerance; flattener.move_to = move_to; flattener.line_to = line_to; @@ -847,10 +1006,8 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path, buf->points[2].y == buf->points[3].y && buf->points[3].x == buf->points[0].x) { - if (box) { - box->p1 = buf->points[0]; - box->p2 = buf->points[2]; - } + box->p1 = buf->points[0]; + box->p2 = buf->points[2]; return TRUE; } @@ -859,10 +1016,8 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path, buf->points[2].x == buf->points[3].x && buf->points[3].y == buf->points[0].y) { - if (box) { - box->p1 = buf->points[0]; - box->p2 = buf->points[2]; - } + box->p1 = buf->points[0]; + box->p2 = buf->points[2]; return TRUE; } @@ -894,3 +1049,216 @@ _cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path, return FALSE; } + +void +_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, + cairo_path_fixed_t *path) +{ + iter->buf = &path->buf_head.base; + iter->n_op = 0; + iter->n_point = 0; +} + +static cairo_bool_t +_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter) +{ + if (++iter->n_op >= iter->buf->num_ops) { + iter->buf = iter->buf->next; + iter->n_op = 0; + iter->n_point = 0; + } + + return iter->buf != NULL; +} + +cairo_bool_t +_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, + cairo_box_t *box) +{ + cairo_point_t points[5]; + cairo_path_fixed_iter_t iter; + + if (_iter->buf == NULL) + return FALSE; + + iter = *_iter; + + if (iter.n_op == iter.buf->num_ops && + ! _cairo_path_fixed_iter_next_op (&iter)) + { + return FALSE; + } + + /* Check whether the ops are those that would be used for a rectangle */ + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO) + return FALSE; + points[0] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[1] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[2] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[3] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + /* Now, there are choices. The rectangle might end with a LINE_TO + * (to the original point), but this isn't required. If it + * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */ + if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) + { + points[4] = iter.buf->points[iter.n_point++]; + if (points[4].x != points[0].x || points[4].y != points[0].y) + return FALSE; + } + else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH || + iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO)) + { + return FALSE; + } + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + /* Ok, we may have a box, if the points line up */ + if (points[0].y == points[1].y && + points[1].x == points[2].x && + points[2].y == points[3].y && + points[3].x == points[0].x) + { + box->p1 = points[0]; + box->p2 = points[2]; + *_iter = iter; + return TRUE; + } + + if (points[0].x == points[1].x && + points[1].y == points[2].y && + points[2].x == points[3].x && + points[3].y == points[0].y) + { + box->p1 = points[0]; + box->p2 = points[2]; + *_iter = iter; + return TRUE; + } + + return FALSE; +} + +cairo_bool_t +_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter) +{ + if (iter->buf == NULL) + return TRUE; + + if (iter->n_op == iter->buf->num_ops) + return TRUE; + + if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO && + iter->buf->num_ops == iter->n_op + 1) + { + return TRUE; + } + + return FALSE; +} + +/* Closure for path region testing. Every move_to must be to integer + * coordinates, there must be no curves, and every line_to or + * close_path must represent an axis aligned line to an integer point. + * We're relying on the path interpreter always sending a single + * move_to at the start of any subpath, not receiving having any + * superfluous move_tos, and the path intepreter bailing with our + * first non-successful error. */ +typedef struct cairo_path_region_tester { + cairo_point_t last_move_point; + cairo_point_t current_point; +} cprt_t; + +static cairo_status_t +_cprt_line_to (void *closure, + const cairo_point_t *p2) +{ + cprt_t *self = closure; + cairo_point_t *p1 = &self->current_point; + + if (p2->x == p1->x) { + if (_cairo_fixed_is_integer (p2->y)) { + p1->y = p2->y; + return CAIRO_STATUS_SUCCESS; + } + } else if (p2->y == p1->y) { + if (_cairo_fixed_is_integer (p2->x)) { + p1->x = p2->x; + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cprt_close_path (void *closure) +{ + cprt_t *self = closure; + return _cprt_line_to (closure, &self->last_move_point); +} + +static cairo_status_t +_cprt_move_to (void *closure, + const cairo_point_t *p) +{ + cprt_t *self = closure; + cairo_status_t status; + + status = _cprt_close_path (closure); + if (status) + return status; + + if (_cairo_fixed_is_integer (p->x) && _cairo_fixed_is_integer (p->y)) { + self->current_point = *p; + self->last_move_point = *p; + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +/* + * Check whether the given path is representable as a region. + * That is, if the path contains only axis aligned lines between + * integer coordinates in device space. + */ +cairo_bool_t +_cairo_path_fixed_is_region (cairo_path_fixed_t *path) +{ + cprt_t cprt; + + if (path->has_curve_to) + return FALSE; + + cprt.current_point.x = 0; + cprt.current_point.y = 0; + cprt.last_move_point.x = 0; + cprt.last_move_point.y = 0; + + return _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cprt_move_to, + _cprt_line_to, + NULL, + _cprt_close_path, + &cprt) == CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c new file mode 100644 index 0000000..24f43ca --- /dev/null +++ b/src/cairo-path-in-fill.c @@ -0,0 +1,286 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * 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 Chris Wilson. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" + +typedef struct cairo_in_fill { + double tolerance; + int winding; + + cairo_fixed_t x, y; + cairo_bool_t on_edge; + + cairo_bool_t has_current_point; + cairo_point_t current_point; + cairo_point_t first_point; +} cairo_in_fill_t; + +static void +_cairo_in_fill_init (cairo_in_fill_t *in_fill, + double tolerance, + double x, + double y) +{ + in_fill->winding = 0; + in_fill->tolerance = tolerance; + + in_fill->x = _cairo_fixed_from_double (x); + in_fill->y = _cairo_fixed_from_double (y); + in_fill->on_edge = FALSE; + + in_fill->has_current_point = FALSE; + in_fill->current_point.x = 0; + in_fill->current_point.y = 0; +} + +static void +_cairo_in_fill_fini (cairo_in_fill_t *in_fill) +{ +} + +static int +edge_compare_for_y_against_x (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t y, + cairo_fixed_t x) +{ + cairo_fixed_t adx, ady; + cairo_fixed_t dx, dy; + cairo_int64_t L, R; + + adx = p2->x - p1->x; + dx = x - p1->x; + + if (adx == 0) + return -dx; + if ((adx ^ dx) < 0) + return adx; + + dy = y - p1->y; + ady = p2->y - p1->y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +static void +_cairo_in_fill_add_edge (cairo_in_fill_t *in_fill, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int dir; + + if (in_fill->on_edge) + return; + + /* count the number of edge crossing to -∞ */ + + dir = 1; + if (p2->y < p1->y) { + const cairo_point_t *tmp; + + tmp = p1; + p1 = p2; + p2 = tmp; + + dir = -1; + } + + /* First check whether the query is on an edge */ + if ((p1->x == in_fill->x && p1->y == in_fill->y) || + (p2->x == in_fill->x && p2->y == in_fill->y) || + (! (p2->y < in_fill->y || p1->y > in_fill->y || + (p1->x > in_fill->x && p2->x > in_fill->x) || + (p1->x < in_fill->x && p2->x < in_fill->x)) && + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0)) + { + in_fill->on_edge = TRUE; + return; + } + + /* edge is entirely above or below, note the shortening rule */ + if (p2->y <= in_fill->y || p1->y > in_fill->y) + return; + + /* edge lies wholly to the right */ + if (p1->x >= in_fill->x && p2->x >= in_fill->x) + return; + + if ((p1->x <= in_fill->x && p2->x <= in_fill->x) || + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) <= 0) + { + in_fill->winding += dir; + } +} + +static cairo_status_t +_cairo_in_fill_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_in_fill_t *in_fill = closure; + + /* implicit close path */ + if (in_fill->has_current_point) { + _cairo_in_fill_add_edge (in_fill, + &in_fill->current_point, + &in_fill->first_point); + } + + in_fill->first_point = *point; + in_fill->current_point = *point; + in_fill->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_in_fill_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_in_fill_t *in_fill = closure; + + if (in_fill->has_current_point) + _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point); + + in_fill->current_point = *point; + in_fill->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_in_fill_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_in_fill_t *in_fill = closure; + cairo_spline_t spline; + cairo_fixed_t top, bot, left; + + /* first reject based on bbox */ + bot = top = in_fill->current_point.y; + if (b->y < top) top = b->y; + if (b->y > bot) bot = b->y; + if (c->y < top) top = c->y; + if (c->y > bot) bot = c->y; + if (d->y < top) top = d->y; + if (d->y > bot) bot = d->y; + if (bot < in_fill->y || top > in_fill->y) { + in_fill->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } + + left = in_fill->current_point.x; + if (b->x < left) left = b->x; + if (c->x < left) left = c->x; + if (d->x < left) left = d->x; + if (left > in_fill->x) { + in_fill->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } + + /* XXX Investigate direct inspection of the inflections? */ + if (! _cairo_spline_init (&spline, + _cairo_in_fill_line_to, + in_fill, + &in_fill->current_point, b, c, d)) + { + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_spline_decompose (&spline, in_fill->tolerance); +} + +static cairo_status_t +_cairo_in_fill_close_path (void *closure) +{ + cairo_in_fill_t *in_fill = closure; + + if (in_fill->has_current_point) { + _cairo_in_fill_add_edge (in_fill, + &in_fill->current_point, + &in_fill->first_point); + + in_fill->has_current_point = FALSE; + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_path_fixed_in_fill (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + double x, + double y, + cairo_bool_t *is_inside) +{ + cairo_in_fill_t in_fill; + cairo_status_t status; + + _cairo_in_fill_init (&in_fill, tolerance, x, y); + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_in_fill_move_to, + _cairo_in_fill_line_to, + _cairo_in_fill_curve_to, + _cairo_in_fill_close_path, + &in_fill); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_in_fill_close_path (&in_fill); + + if (in_fill.on_edge) { + *is_inside = TRUE; + } else switch (fill_rule) { + case CAIRO_FILL_RULE_EVEN_ODD: + *is_inside = in_fill.winding & 1; + break; + case CAIRO_FILL_RULE_WINDING: + *is_inside = in_fill.winding != 0; + break; + default: + ASSERT_NOT_REACHED; + *is_inside = FALSE; + break; + } + + _cairo_in_fill_fini (&in_fill); +} diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index efccbcf..79bf09b 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -35,14 +35,27 @@ * Carl D. Worth <cworth@cworth.org> */ +#define _BSD_SOURCE /* for hypot() */ #include "cairoint.h" #include "cairo-path-fixed-private.h" +typedef struct _cairo_stroker_dash { + cairo_bool_t dashed; + unsigned int dash_index; + cairo_bool_t dash_on; + cairo_bool_t dash_starts_on; + double dash_remain; + + double dash_offset; + const double *dashes; + unsigned int num_dashes; +} cairo_stroker_dash_t; + typedef struct cairo_stroker { - cairo_stroke_style_t *style; + cairo_stroke_style_t *style; - cairo_matrix_t *ctm; - cairo_matrix_t *ctm_inverse; + const cairo_matrix_t *ctm; + const cairo_matrix_t *ctm_inverse; double tolerance; double ctm_determinant; cairo_bool_t ctm_det_positive; @@ -62,107 +75,77 @@ typedef struct cairo_stroker { cairo_bool_t has_first_face; cairo_stroke_face_t first_face; - cairo_bool_t dashed; - unsigned int dash_index; - cairo_bool_t dash_on; - cairo_bool_t dash_starts_on; - double dash_remain; + cairo_stroker_dash_t dash; cairo_bool_t has_bounds; cairo_box_t bounds; } cairo_stroker_t; -/* private functions */ -static cairo_status_t -_cairo_stroker_init (cairo_stroker_t *stroker, - cairo_stroke_style_t *stroke_style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_traps_t *traps); - static void -_cairo_stroker_fini (cairo_stroker_t *stroker); - -static cairo_status_t -_cairo_stroker_move_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_stroker_line_to (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point); - -static cairo_status_t -_cairo_stroker_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -static cairo_status_t -_cairo_stroker_curve_to_dashed (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - -static cairo_status_t -_cairo_stroker_close_path (void *closure); - -static void -_translate_point (cairo_point_t *point, cairo_point_t *offset); - -static int -_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out); - -static cairo_status_t -_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out); - -static void -_cairo_stroker_start_dash (cairo_stroker_t *stroker) +_cairo_stroker_dash_start (cairo_stroker_dash_t *dash) { double offset; cairo_bool_t on = TRUE; unsigned int i = 0; - offset = stroker->style->dash_offset; + if (! dash->dashed) + return; + + offset = dash->dash_offset; /* 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]; + while (offset > 0.0 && offset >= dash->dashes[i]) { + offset -= dash->dashes[i]; on = !on; - if (++i == stroker->style->num_dashes) + if (++i == dash->num_dashes) i = 0; } - stroker->dashed = TRUE; - stroker->dash_index = i; - stroker->dash_on = stroker->dash_starts_on = on; - stroker->dash_remain = stroker->style->dash[i] - offset; + + dash->dash_index = i; + dash->dash_on = dash->dash_starts_on = on; + dash->dash_remain = dash->dashes[i] - offset; } static void -_cairo_stroker_step_dash (cairo_stroker_t *stroker, double step) +_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) { - stroker->dash_remain -= step; - if (stroker->dash_remain <= 0) { - stroker->dash_index++; - if (stroker->dash_index == stroker->style->num_dashes) - stroker->dash_index = 0; - stroker->dash_on = !stroker->dash_on; - stroker->dash_remain = stroker->style->dash[stroker->dash_index]; + dash->dash_remain -= step; + if (dash->dash_remain <= 0.) { + if (++dash->dash_index == dash->num_dashes) + dash->dash_index = 0; + + dash->dash_on = ! dash->dash_on; + dash->dash_remain = dash->dashes[dash->dash_index]; } } +static void +_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, + const cairo_stroke_style_t *style) +{ + dash->dashed = style->dash != NULL; + if (! dash->dashed) + return; + + dash->dashes = style->dash; + dash->num_dashes = style->num_dashes; + dash->dash_offset = style->dash_offset; + + _cairo_stroker_dash_start (dash); +} + static cairo_status_t _cairo_stroker_init (cairo_stroker_t *stroker, cairo_stroke_style_t *stroke_style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps) { cairo_status_t status; + stroker->style = stroke_style; stroker->ctm = ctm; stroker->ctm_inverse = ctm_inverse; @@ -175,17 +158,14 @@ _cairo_stroker_init (cairo_stroker_t *stroker, status = _cairo_pen_init (&stroker->pen, stroke_style->line_width / 2.0, tolerance, ctm); - if (status) + if (unlikely (status)) return status; stroker->has_current_face = FALSE; stroker->has_first_face = FALSE; stroker->has_initial_sub_path = FALSE; - if (stroker->style->dash) - _cairo_stroker_start_dash (stroker); - else - stroker->dashed = FALSE; + _cairo_stroker_dash_init (&stroker->dash, stroke_style); stroker->has_bounds = _cairo_traps_get_limit (traps, &stroker->bounds); if (stroker->has_bounds) { @@ -196,14 +176,15 @@ _cairo_stroker_init (cairo_stroker_t *stroker, double dx, dy; cairo_fixed_t fdx, fdy; - _cairo_stroke_style_max_distance_from_path (stroker->style, stroker->ctm, &dx, &dy); + _cairo_stroke_style_max_distance_from_path (stroker->style, + stroker->ctm, + &dx, &dy); fdx = _cairo_fixed_from_double (dx); - fdy = _cairo_fixed_from_double (dy); - stroker->bounds.p1.x -= fdx; stroker->bounds.p2.x += fdx; + fdy = _cairo_fixed_from_double (dy); stroker->bounds.p1.y -= fdy; stroker->bounds.p2.y += fdy; } @@ -267,11 +248,11 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st } if (clockwise) { - inpt = &in->ccw; - outpt = &out->ccw; + inpt = &in->ccw; + outpt = &out->ccw; } else { - inpt = &in->cw; - outpt = &out->cw; + inpt = &in->cw; + outpt = &out->cw; } switch (stroker->style->line_join) { @@ -283,13 +264,17 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st tri[0] = in->point; if (clockwise) { - _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector, &start); + start = + _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector); + stop = + _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector); step = -1; - _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector, &stop); } else { - _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector, &start); + start = + _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector); + stop = + _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector); step = +1; - _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector, &stop); } i = start; @@ -298,7 +283,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st tri[2] = in->point; _translate_point (&tri[2], &pen->vertices[i].point); status = _cairo_traps_tessellate_triangle (stroker->traps, tri); - if (status) + if (unlikely (status)) return status; tri[1] = tri[2]; i += step; @@ -338,7 +323,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st * | \ * | \ * | \ - * miter \ + * miter \ * length \ * | \ * | .\ @@ -494,10 +479,10 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) cairo_pen_t *pen = &stroker->pen; slope = f->dev_vector; - _cairo_pen_find_active_cw_vertex_index (pen, &slope, &start); + start = _cairo_pen_find_active_cw_vertex_index (pen, &slope); slope.dx = -slope.dx; slope.dy = -slope.dy; - _cairo_pen_find_active_cw_vertex_index (pen, &slope, &stop); + stop = _cairo_pen_find_active_cw_vertex_index (pen, &slope); tri[0] = f->point; tri[1] = f->cw; @@ -505,7 +490,7 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) tri[2] = f->point; _translate_point (&tri[2], &pen->vertices[i].point); status = _cairo_traps_tessellate_triangle (stroker->traps, tri); - if (status) + if (unlikely (status)) return status; tri[1] = tri[2]; } @@ -584,7 +569,9 @@ _cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker, } static inline cairo_bool_t -_compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_inverse, double *mag_out) +_compute_normalized_device_slope (double *dx, double *dy, + const cairo_matrix_t *ctm_inverse, + double *mag_out) { double dx0 = *dx, dy0 = *dy; double mag; @@ -616,7 +603,7 @@ _compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_in *dx = -1.0; } } else { - mag = sqrt (dx0 * dx0 + dy0 * dy0); + mag = hypot (dx0, dy0); *dx = dx0 / mag; *dy = dy0 / mag; } @@ -628,7 +615,7 @@ _compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_in } static void -_compute_face (cairo_point_t *point, cairo_slope_t *dev_slope, +_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, double slope_dx, double slope_dy, cairo_stroker_t *stroker, cairo_stroke_face_t *face); @@ -654,22 +641,22 @@ _cairo_stroker_add_caps (cairo_stroker_t *stroker) _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); status = _cairo_stroker_add_leading_cap (stroker, &face); - if (status) + if (unlikely (status)) return status; status = _cairo_stroker_add_trailing_cap (stroker, &face); - if (status) + if (unlikely (status)) return status; } if (stroker->has_first_face) { status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face); - if (status) + if (unlikely (status)) return status; } if (stroker->has_current_face) { status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); - if (status) + if (unlikely (status)) return status; } @@ -677,7 +664,7 @@ _cairo_stroker_add_caps (cairo_stroker_t *stroker) } static void -_compute_face (cairo_point_t *point, cairo_slope_t *dev_slope, +_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, double slope_dx, double slope_dy, cairo_stroker_t *stroker, cairo_stroke_face_t *face) { @@ -725,9 +712,13 @@ _compute_face (cairo_point_t *point, cairo_slope_t *dev_slope, } static cairo_status_t -_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_point_t *p2, - cairo_slope_t *dev_slope, double slope_dx, double slope_dy, - cairo_stroke_face_t *start, cairo_stroke_face_t *end) +_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, + const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_slope_t *dev_slope, + double slope_dx, double slope_dy, + cairo_stroke_face_t *start, + cairo_stroke_face_t *end) { cairo_point_t rectangle[4]; @@ -750,14 +741,15 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_ } static cairo_status_t -_cairo_stroker_move_to (void *closure, cairo_point_t *point) +_cairo_stroker_move_to (void *closure, + const cairo_point_t *point) { - cairo_status_t status; cairo_stroker_t *stroker = closure; + cairo_status_t status; /* Cap the start and end of the previous sub path as needed */ status = _cairo_stroker_add_caps (stroker); - if (status) + if (unlikely (status)) return status; stroker->first_point = *point; @@ -771,23 +763,25 @@ _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) +_cairo_stroker_move_to_dashed (void *closure, + const cairo_point_t *point) { - /* reset the dash pattern for new sub paths */ cairo_stroker_t *stroker = closure; - _cairo_stroker_start_dash (stroker); + + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); return _cairo_stroker_move_to (closure, point); } static cairo_status_t -_cairo_stroker_line_to (void *closure, cairo_point_t *point) +_cairo_stroker_line_to (void *closure, + const cairo_point_t *p2) { cairo_status_t status; cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; cairo_point_t *p1 = &stroker->current_point; - cairo_point_t *p2 = point; cairo_slope_t dev_slope; double slope_dx, slope_dy; @@ -801,14 +795,18 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point) slope_dy = _cairo_fixed_to_double (p2->y - p1->y); _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL); - status = _cairo_stroker_add_sub_edge (stroker, p1, p2, &dev_slope, slope_dx, slope_dy, &start, &end); - if (status) + status = _cairo_stroker_add_sub_edge (stroker, + p1, p2, + &dev_slope, + slope_dx, slope_dy, + &start, &end); + if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &start); - if (status) + if (unlikely (status)) return status; } else if (!stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ @@ -818,7 +816,7 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point) stroker->current_face = end; stroker->has_current_face = TRUE; - stroker->current_point = *point; + stroker->current_point = *p2; return CAIRO_STATUS_SUCCESS; } @@ -827,7 +825,8 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point) * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t -_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) +_cairo_stroker_line_to_dashed (void *closure, + const cairo_point_t *p2) { cairo_stroker_t *stroker = closure; double mag, remain, step_length = 0; @@ -835,13 +834,12 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) double dx2, dy2; cairo_stroke_face_t sub_start, sub_end; cairo_point_t *p1 = &stroker->current_point; - cairo_point_t *p2 = point; cairo_slope_t dev_slope; cairo_line_t segment; cairo_bool_t fully_in_bounds; cairo_status_t status; - stroker->has_initial_sub_path = stroker->dash_starts_on; + stroker->has_initial_sub_path = stroker->dash.dash_starts_on; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; @@ -868,7 +866,7 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) remain = mag; segment.p1 = *p1; while (remain) { - step_length = MIN (stroker->dash_remain, remain); + step_length = MIN (stroker->dash.dash_remain, remain); remain -= step_length; dx2 = slope_dx * (mag - remain); dy2 = slope_dy * (mag - remain); @@ -876,9 +874,9 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; - if (stroker->dash_on && + if (stroker->dash.dash_on && (fully_in_bounds || - (! stroker->has_first_face && stroker->dash_starts_on) || + (! stroker->has_first_face && stroker->dash.dash_starts_on) || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { status = _cairo_stroker_add_sub_edge (stroker, @@ -886,33 +884,39 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) &dev_slope, slope_dx, slope_dy, &sub_start, &sub_end); - if (status) + if (unlikely (status)) return status; - if (stroker->has_current_face) { + if (stroker->has_current_face) + { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); - if (status) + if (unlikely (status)) return status; stroker->has_current_face = FALSE; - } else if (! stroker->has_first_face && stroker->dash_starts_on) { + } + else if (! stroker->has_first_face && + stroker->dash.dash_starts_on) + { /* Save sub path's first face in case needed for closing join */ stroker->first_face = sub_start; stroker->has_first_face = TRUE; - } else { + } + else + { /* Cap dash start if not connecting to a previous segment */ status = _cairo_stroker_add_leading_cap (stroker, &sub_start); - if (status) + if (unlikely (status)) return status; } if (remain) { /* Cap dash end if not at end of segment */ status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); - if (status) + if (unlikely (status)) return status; } else { stroker->current_face = sub_end; @@ -923,18 +927,18 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) /* Cap final face from previous segment */ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); - if (status) + if (unlikely (status)) return status; stroker->has_current_face = FALSE; } } - _cairo_stroker_step_dash (stroker, step_length); + _cairo_stroker_dash_step (&stroker->dash, step_length); segment.p1 = segment.p2; } - if (stroker->dash_on && ! stroker->has_current_face) { + if (stroker->dash.dash_on && ! stroker->has_current_face) { /* This segment ends on a transition to dash_on, compute a new face * and add cap for the beginning of the next dash_on step. * @@ -944,64 +948,75 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) * path stroking. * On the other hand, Acroread 7 also produces the degenerate caps. */ - _compute_face (point, &dev_slope, + _compute_face (p2, &dev_slope, slope_dx, slope_dy, stroker, &stroker->current_face); status = _cairo_stroker_add_leading_cap (stroker, &stroker->current_face); - if (status) + if (unlikely (status)) return status; stroker->has_current_face = TRUE; } - stroker->current_point = *point; + stroker->current_point = *p2; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; - cairo_spline_t spline; - cairo_pen_t pen; + cairo_pen_stroke_spline_t spline_pen; cairo_stroke_face_t start, end; cairo_point_t extra_points[4]; cairo_point_t *a = &stroker->current_point; double initial_slope_dx, initial_slope_dy; double final_slope_dx, final_slope_dy; + cairo_status_t status; - status = _cairo_spline_init (&spline, a, b, c, d); + status = _cairo_pen_stroke_spline_init (&spline_pen, + &stroker->pen, + a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) return _cairo_stroker_line_to (closure, d); + else if (unlikely (status)) + return status; - status = _cairo_pen_init_copy (&pen, &stroker->pen); - if (status) - goto CLEANUP_SPLINE; - - initial_slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); - initial_slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); - final_slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); - final_slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); + initial_slope_dx = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dx); + initial_slope_dy = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dy); + final_slope_dx = _cairo_fixed_to_double (spline_pen.spline.final_slope.dx); + final_slope_dy = _cairo_fixed_to_double (spline_pen.spline.final_slope.dy); - if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy, stroker->ctm_inverse, NULL)) - _compute_face (a, &spline.initial_slope, initial_slope_dx, initial_slope_dy, stroker, &start); + if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (a, + &spline_pen.spline.initial_slope, + initial_slope_dx, initial_slope_dy, + stroker, &start); + } - if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy, stroker->ctm_inverse, NULL)) - _compute_face (d, &spline.final_slope, final_slope_dx, final_slope_dy, stroker, &end); + if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (d, + &spline_pen.spline.final_slope, + final_slope_dx, final_slope_dy, + stroker, &end); + } if (stroker->has_current_face) { status = _cairo_stroker_join (stroker, &stroker->current_face, &start); - if (status) + if (unlikely (status)) goto CLEANUP_PEN; - } else if (!stroker->has_first_face) { + } else if (! stroker->has_first_face) { stroker->first_face = start; stroker->has_first_face = TRUE; } @@ -1021,18 +1036,16 @@ _cairo_stroker_curve_to (void *closure, extra_points[3].x -= end.point.x; extra_points[3].y -= end.point.y; - status = _cairo_pen_add_points (&pen, extra_points, 4); - if (status) + status = _cairo_pen_add_points (&spline_pen.pen, extra_points, 4); + if (unlikely (status)) goto CLEANUP_PEN; - status = _cairo_pen_stroke_spline (&pen, &spline, stroker->tolerance, stroker->traps); - if (status) - goto CLEANUP_PEN; + status = _cairo_pen_stroke_spline (&spline_pen, + stroker->tolerance, + stroker->traps); CLEANUP_PEN: - _cairo_pen_fini (&pen); - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); + _cairo_pen_stroke_spline_fini (&spline_pen); stroker->current_point = *d; @@ -1059,25 +1072,28 @@ _cairo_stroker_curve_to (void *closure, */ static cairo_status_t _cairo_stroker_curve_to_dashed (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; cairo_spline_t spline; cairo_point_t *a = &stroker->current_point; cairo_line_join_t line_join_save; - int i; + cairo_status_t status; - status = _cairo_spline_init (&spline, a, b, c, d); - if (status == CAIRO_INT_STATUS_DEGENERATE) + if (! _cairo_spline_init (&spline, + _cairo_stroker_line_to_dashed, + stroker, + a, b, c, d)) + { return _cairo_stroker_line_to_dashed (closure, d); + } /* If the line width is so small that the pen is reduced to a single point, then we have nothing to do. */ if (stroker->pen.num_vertices <= 1) - goto CLEANUP_SPLINE; + return CAIRO_STATUS_SUCCESS; /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ @@ -1085,24 +1101,9 @@ _cairo_stroker_curve_to_dashed (void *closure, stroker->style->line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); - if (status) - goto CLEANUP_GSTATE; - for (i = 1; i < spline.num_points; i++) { - if (stroker->dashed) - status = _cairo_stroker_line_to_dashed (stroker, &spline.points[i]); - else - status = _cairo_stroker_line_to (stroker, &spline.points[i]); - if (status) - break; - } - - CLEANUP_GSTATE: stroker->style->line_join = line_join_save; - CLEANUP_SPLINE: - _cairo_spline_fini (&spline); - return status; } @@ -1112,22 +1113,22 @@ _cairo_stroker_close_path (void *closure) cairo_status_t status; cairo_stroker_t *stroker = closure; - if (stroker->dashed) + if (stroker->dash.dashed) status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); else status = _cairo_stroker_line_to (stroker, &stroker->first_point); - if (status) + if (unlikely (status)) return status; if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); - if (status) + if (unlikely (status)) return status; } else { /* Cap the start and end of the sub path as needed */ status = _cairo_stroker_add_caps (stroker); - if (status) + if (unlikely (status)) return status; } @@ -1141,14 +1142,14 @@ _cairo_stroker_close_path (void *closure) static cairo_int_status_t _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, - cairo_matrix_t *ctm, + const cairo_matrix_t *ctm, cairo_traps_t *traps); cairo_status_t _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps) { @@ -1170,7 +1171,7 @@ _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, status = _cairo_stroker_init (&stroker, stroke_style, ctm, ctm_inverse, tolerance, traps); - if (status) + if (unlikely (status)) return status; if (stroker.style->dash) @@ -1189,7 +1190,7 @@ _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, _cairo_stroker_curve_to, _cairo_stroker_close_path, &stroker); - if (status) + if (unlikely (status)) goto BAIL; /* Cap the start and end of the final sub path as needed */ @@ -1201,26 +1202,56 @@ BAIL: return status; } -typedef struct _cairo_rectilinear_stroker -{ +typedef struct _segment_t { + cairo_point_t p1, p2; + cairo_bool_t is_horizontal; + cairo_bool_t has_join; +} segment_t; + +typedef struct _cairo_rectilinear_stroker { cairo_stroke_style_t *stroke_style; + const cairo_matrix_t *ctm; + cairo_fixed_t half_line_width; cairo_traps_t *traps; cairo_point_t current_point; cairo_point_t first_point; cairo_bool_t open_sub_path; + + cairo_stroker_dash_t dash; + + cairo_bool_t has_bounds; + cairo_box_t bounds; + int num_segments; int segments_size; - cairo_line_t *segments; - cairo_line_t segments_embedded[8]; /* common case is a single rectangle */ + segment_t *segments; + segment_t segments_embedded[8]; /* common case is a single rectangle */ } cairo_rectilinear_stroker_t; static void +_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, + const cairo_box_t *box) +{ + stroker->has_bounds = TRUE; + stroker->bounds = *box; + + stroker->bounds.p1.x -= stroker->half_line_width; + stroker->bounds.p2.x += stroker->half_line_width; + + stroker->bounds.p1.y -= stroker->half_line_width; + stroker->bounds.p2.y += stroker->half_line_width; +} + +static void _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, cairo_traps_t *traps) { stroker->stroke_style = stroke_style; + stroker->ctm = ctm; + stroker->half_line_width = _cairo_fixed_from_double (stroke_style->line_width / 2.0); stroker->traps = traps; @@ -1228,6 +1259,10 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, stroker->segments = stroker->segments_embedded; stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); stroker->num_segments = 0; + + _cairo_stroker_dash_init (&stroker->dash, stroke_style); + + stroker->has_bounds = FALSE; } static void @@ -1238,26 +1273,30 @@ _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) } static cairo_status_t -_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, - cairo_point_t *p1, - cairo_point_t *p2) +_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, + const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_bool_t is_horizontal, + cairo_bool_t has_join) { + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (stroker->num_segments == stroker->segments_size) { int new_size = stroker->segments_size * 2; - cairo_line_t *new_segments; + segment_t *new_segments; if (stroker->segments == stroker->segments_embedded) { - new_segments = _cairo_malloc_ab (new_size, sizeof (cairo_line_t)); - if (new_segments == NULL) + new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); + if (unlikely (new_segments == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (new_segments, stroker->segments, - stroker->num_segments * sizeof (cairo_line_t)); + stroker->num_segments * sizeof (segment_t)); } else { new_segments = _cairo_realloc_ab (stroker->segments, - new_size, sizeof (cairo_line_t)); - if (new_segments == NULL) + new_size, sizeof (segment_t)); + if (unlikely (new_segments == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1267,6 +1306,8 @@ _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, stroker->segments[stroker->num_segments].p1 = *p1; stroker->segments[stroker->num_segments].p2 = *p2; + stroker->segments[stroker->num_segments].has_join = has_join; + stroker->segments[stroker->num_segments].is_horizontal = is_horizontal; stroker->num_segments++; return CAIRO_STATUS_SUCCESS; @@ -1381,7 +1422,127 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) } status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b); - if (status) + if (unlikely (status)) + return status; + } + + stroker->num_segments = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) +{ + cairo_status_t status; + cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; + cairo_fixed_t half_line_width = stroker->half_line_width; + int i; + + for (i = 0; i < stroker->num_segments; i++) { + cairo_point_t *a, *b; + cairo_bool_t is_horizontal; + + a = &stroker->segments[i].p1; + b = &stroker->segments[i].p2; + + is_horizontal = stroker->segments[i].is_horizontal; + + /* Handle the joins for a potentially degenerate segment. */ + if (line_cap == CAIRO_LINE_CAP_BUTT && + stroker->segments[i].has_join && + (i != stroker->num_segments - 1 || + (! stroker->open_sub_path && stroker->dash.dash_starts_on))) + { + cairo_point_t p1 = stroker->segments[i].p1; + cairo_point_t p2 = stroker->segments[i].p2; + cairo_slope_t out_slope; + int j = (i + 1) % stroker->num_segments; + + _cairo_slope_init (&out_slope, + &stroker->segments[j].p1, + &stroker->segments[j].p2); + + if (is_horizontal) { + if (p1.x <= p2.x) { + p1.x = p2.x; + p2.x += half_line_width; + } else { + p1.x = p2.x - half_line_width; + } + if (out_slope.dy >= 0) + p1.y -= half_line_width; + if (out_slope.dy <= 0) + p2.y += half_line_width; + } else { + if (p1.y <= p2.y) { + p1.y = p2.y; + p2.y += half_line_width; + } else { + p1.y = p2.y - half_line_width; + } + if (out_slope.dx >= 0) + p1.x -= half_line_width; + if (out_slope.dx <= 0) + p2.x += half_line_width; + } + + status = _cairo_traps_tessellate_rectangle (stroker->traps, + &p1, &p2); + if (unlikely (status)) + return status; + } + + /* Perform the adjustments of the endpoints. */ + if (is_horizontal) { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { + if (a->x <= b->x) { + a->x -= half_line_width; + b->x += half_line_width; + } else { + a->x += half_line_width; + b->x -= half_line_width; + } + } + + if (a->x > b->x) { + cairo_point_t *t; + + t = a; + a = b; + b = t; + } + + a->y -= half_line_width; + b->y += half_line_width; + } else { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { + if (a->y <= b->y) { + a->y -= half_line_width; + b->y += half_line_width; + } else { + a->y += half_line_width; + b->y -= half_line_width; + } + } + + if (a->y > b->y) { + cairo_point_t *t; + + t = a; + a = b; + b = t; + } + + a->x -= half_line_width; + b->x += half_line_width; + } + + if (a->x == b->x && a->y == b->y) + continue; + + status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b); + if (unlikely (status)) return status; } @@ -1392,15 +1553,21 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) static cairo_status_t _cairo_rectilinear_stroker_move_to (void *closure, - cairo_point_t *point) + const cairo_point_t *point) { cairo_rectilinear_stroker_t *stroker = closure; cairo_status_t status; - status = _cairo_rectilinear_stroker_emit_segments (stroker); - if (status) + if (stroker->dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (stroker); + if (unlikely (status)) return status; + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); + stroker->current_point = *point; stroker->first_point = *point; @@ -1409,11 +1576,10 @@ _cairo_rectilinear_stroker_move_to (void *closure, static cairo_status_t _cairo_rectilinear_stroker_line_to (void *closure, - cairo_point_t *point) + const cairo_point_t *b) { cairo_rectilinear_stroker_t *stroker = closure; cairo_point_t *a = &stroker->current_point; - cairo_point_t *b = point; cairo_status_t status; /* We only support horizontal or vertical elements. */ @@ -1424,15 +1590,121 @@ _cairo_rectilinear_stroker_line_to (void *closure, if (a->x == b->x && a->y == b->y) return CAIRO_STATUS_SUCCESS; - status = _cairo_rectilinear_stroker_add_segment (stroker, a, b); + status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, + a->y == b->y, + TRUE); - stroker->current_point = *point; + stroker->current_point = *b; stroker->open_sub_path = TRUE; return status; } static cairo_status_t +_cairo_rectilinear_stroker_line_to_dashed (void *closure, + const cairo_point_t *point) +{ + cairo_rectilinear_stroker_t *stroker = closure; + const cairo_point_t *a = &stroker->current_point; + const cairo_point_t *b = point; + cairo_bool_t fully_in_bounds; + double sign, remain; + cairo_fixed_t mag; + cairo_status_t status; + cairo_line_t segment; + cairo_bool_t dash_on = FALSE; + cairo_bool_t is_horizontal; + + /* We don't draw anything for degenerate paths. */ + if (a->x == b->x && a->y == b->y) + return CAIRO_STATUS_SUCCESS; + + /* We only support horizontal or vertical elements. */ + if (! (a->x == b->x || a->y == b->y)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + fully_in_bounds = TRUE; + if (stroker->has_bounds && + (! _cairo_box_contains_point (&stroker->bounds, a) || + ! _cairo_box_contains_point (&stroker->bounds, b))) + { + fully_in_bounds = FALSE; + } + + is_horizontal = a->y == b->y; + if (is_horizontal) + mag = b->x - a->x; + else + mag = b->y - a->y; + if (mag < 0) { + remain = _cairo_fixed_to_double (-mag); + sign = 1.; + } else { + remain = _cairo_fixed_to_double (mag); + sign = -1.; + } + + segment.p2 = segment.p1 = *a; + while (remain > 0.) { + double step_length; + + step_length = MIN (stroker->dash.dash_remain, remain); + remain -= step_length; + + mag = _cairo_fixed_from_double (sign*remain); + if (is_horizontal) + segment.p2.x = b->x + mag; + else + segment.p2.y = b->y + mag; + + if (stroker->dash.dash_on && + (fully_in_bounds || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + status = _cairo_rectilinear_stroker_add_segment (stroker, + &segment.p1, + &segment.p2, + is_horizontal, + remain <= 0.); + if (unlikely (status)) + return status; + + dash_on = TRUE; + } + else + { + dash_on = FALSE; + } + + _cairo_stroker_dash_step (&stroker->dash, step_length); + segment.p1 = segment.p2; + } + + if (stroker->dash.dash_on && ! dash_on && + (fully_in_bounds || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + + /* This segment ends on a transition to dash_on, compute a new face + * and add cap for the beginning of the next dash_on step. + */ + + status = _cairo_rectilinear_stroker_add_segment (stroker, + &segment.p1, + &segment.p1, + is_horizontal, + TRUE); + if (unlikely (status)) + return status; + } + + stroker->current_point = *point; + stroker->open_sub_path = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t _cairo_rectilinear_stroker_close_path (void *closure) { cairo_rectilinear_stroker_t *stroker = closure; @@ -1442,15 +1714,23 @@ _cairo_rectilinear_stroker_close_path (void *closure) if (! stroker->open_sub_path) return CAIRO_STATUS_SUCCESS; - status = _cairo_rectilinear_stroker_line_to (stroker, - &stroker->first_point); - if (status) + if (stroker->dash.dashed) { + status = _cairo_rectilinear_stroker_line_to_dashed (stroker, + &stroker->first_point); + } else { + status = _cairo_rectilinear_stroker_line_to (stroker, + &stroker->first_point); + } + if (unlikely (status)) return status; stroker->open_sub_path = FALSE; - status = _cairo_rectilinear_stroker_emit_segments (stroker); - if (status) + if (stroker->dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (stroker); + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1459,16 +1739,16 @@ _cairo_rectilinear_stroker_close_path (void *closure) static cairo_int_status_t _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, - cairo_matrix_t *ctm, + const cairo_matrix_t *ctm, cairo_traps_t *traps) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; /* This special-case rectilinear stroker only supports - * miter-joined lines (not curves) and no dashing and a - * translation-only matrix (though it could probably be extended - * to support a matrix with uniform, integer scaling). + * miter-joined lines (not curves) and a translation-only matrix + * (though it could probably be extended to support a matrix with + * uniform, integer scaling). * * It also only supports horizontal and vertical line_to * elements. But we don't catch that here, but instead return @@ -1485,8 +1765,6 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, * which we round for safety. */ if (stroke_style->miter_limit < M_SQRT2) return CAIRO_INT_STATUS_UNSUPPORTED; - if (stroke_style->dash) - return CAIRO_INT_STATUS_UNSUPPORTED; if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) { @@ -1498,24 +1776,36 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, return CAIRO_INT_STATUS_UNSUPPORTED; } - _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, traps); + _cairo_rectilinear_stroker_init (&rectilinear_stroker, + stroke_style, + ctm, + traps); + if (traps->has_limits) { + _cairo_rectilinear_stroker_limit (&rectilinear_stroker, + &traps->limits); + } status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, _cairo_rectilinear_stroker_move_to, + rectilinear_stroker.dash.dashed ? + _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); - if (status) + if (unlikely (status)) goto BAIL; - status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); + if (rectilinear_stroker.dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - if (status) + if (unlikely (status)) _cairo_traps_clear (traps); return status; diff --git a/src/cairo-path.c b/src/cairo-path.c index c6639f3..0544505 100644 --- a/src/cairo-path.c +++ b/src/cairo-path.c @@ -36,6 +36,7 @@ #include "cairoint.h" +#include "cairo-private.h" #include "cairo-path-private.h" #include "cairo-path-fixed-private.h" @@ -48,7 +49,8 @@ typedef struct cairo_path_count { } cpc_t; static cairo_status_t -_cpc_move_to (void *closure, cairo_point_t *point) +_cpc_move_to (void *closure, + const cairo_point_t *point) { cpc_t *cpc = closure; @@ -60,7 +62,8 @@ _cpc_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cpc_line_to (void *closure, cairo_point_t *point) +_cpc_line_to (void *closure, + const cairo_point_t *point) { cpc_t *cpc = closure; @@ -73,9 +76,9 @@ _cpc_line_to (void *closure, cairo_point_t *point) static cairo_status_t _cpc_curve_to (void *closure, - cairo_point_t *p1, - cairo_point_t *p2, - cairo_point_t *p3) + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) { cpc_t *cpc = closure; @@ -127,7 +130,7 @@ _cairo_path_count (cairo_path_t *path, &cpc); } - if (status) + if (unlikely (status)) return -1; return cpc.count; @@ -141,7 +144,8 @@ typedef struct cairo_path_populate { } cpp_t; static cairo_status_t -_cpp_move_to (void *closure, cairo_point_t *point) +_cpp_move_to (void *closure, + const cairo_point_t *point) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; @@ -167,7 +171,8 @@ _cpp_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cpp_line_to (void *closure, cairo_point_t *point) +_cpp_line_to (void *closure, + const cairo_point_t *point) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; @@ -193,10 +198,10 @@ _cpp_line_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cpp_curve_to (void *closure, - cairo_point_t *p1, - cairo_point_t *p2, - cairo_point_t *p3) +_cpp_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; @@ -283,7 +288,7 @@ _cairo_path_populate (cairo_path_t *path, &cpp); } - if (status) + if (unlikely (status)) return status; /* Sanity check the count */ @@ -302,7 +307,7 @@ _cairo_path_create_in_error (cairo_status_t status) return (cairo_path_t*) &_cairo_path_nil; path = malloc (sizeof (cairo_path_t)); - if (path == NULL) { + if (unlikely (path == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; } @@ -322,7 +327,7 @@ _cairo_path_create_internal (cairo_path_fixed_t *path_fixed, cairo_path_t *path; path = malloc (sizeof (cairo_path_t)); - if (path == NULL) { + if (unlikely (path == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; } @@ -337,8 +342,8 @@ _cairo_path_create_internal (cairo_path_fixed_t *path_fixed, if (path->num_data) { path->data = _cairo_malloc_ab (path->num_data, - sizeof (cairo_path_data_t)); - if (path->data == NULL) { + sizeof (cairo_path_data_t)); + if (unlikely (path->data == NULL)) { free (path); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; @@ -437,44 +442,83 @@ cairo_status_t _cairo_path_append_to_context (const cairo_path_t *path, cairo_t *cr) { - int i; - cairo_path_data_t *p; + const cairo_path_data_t *p, *end; + cairo_fixed_t x1_fixed, y1_fixed; + cairo_fixed_t x2_fixed, y2_fixed; + cairo_fixed_t x3_fixed, y3_fixed; + cairo_matrix_t user_to_backend; cairo_status_t status; + double x, y; + + user_to_backend = cr->gstate->ctm; + cairo_matrix_multiply (&user_to_backend, + &user_to_backend, + &cr->gstate->target->device_transform); - for (i=0; i < path->num_data; i += path->data[i].header.length) { - p = &path->data[i]; + end = &path->data[path->num_data]; + for (p = &path->data[0]; p < end; p += p->header.length) { switch (p->header.type) { case CAIRO_PATH_MOVE_TO: - if (p->header.length < 2) + if (unlikely (p->header.length < 2)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - cairo_move_to (cr, - p[1].point.x, p[1].point.y); + + x = p[1].point.x, y = p[1].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x1_fixed = _cairo_fixed_from_double (x); + y1_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed); break; + case CAIRO_PATH_LINE_TO: - if (p->header.length < 2) + if (unlikely (p->header.length < 2)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - cairo_line_to (cr, - p[1].point.x, p[1].point.y); + + x = p[1].point.x, y = p[1].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x1_fixed = _cairo_fixed_from_double (x); + y1_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed); break; + case CAIRO_PATH_CURVE_TO: - if (p->header.length < 4) + if (unlikely (p->header.length < 4)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - cairo_curve_to (cr, - p[1].point.x, p[1].point.y, - p[2].point.x, p[2].point.y, - p[3].point.x, p[3].point.y); + + x = p[1].point.x, y = p[1].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x1_fixed = _cairo_fixed_from_double (x); + y1_fixed = _cairo_fixed_from_double (y); + + x = p[2].point.x, y = p[2].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x2_fixed = _cairo_fixed_from_double (x); + y2_fixed = _cairo_fixed_from_double (y); + + x = p[3].point.x, y = p[3].point.y; + cairo_matrix_transform_point (&user_to_backend, &x, &y); + x3_fixed = _cairo_fixed_from_double (x); + y3_fixed = _cairo_fixed_from_double (y); + + status = _cairo_path_fixed_curve_to (cr->path, + x1_fixed, y1_fixed, + x2_fixed, y2_fixed, + x3_fixed, y3_fixed); break; + case CAIRO_PATH_CLOSE_PATH: - if (p->header.length < 1) + if (unlikely (p->header.length < 1)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - cairo_close_path (cr); + + status = _cairo_path_fixed_close_path (cr->path); break; + default: return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); } - status = cairo_status (cr); - if (status) + if (unlikely (status)) return status; } diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index bd3ed93..38aa253 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -31,7 +31,7 @@ #include "cairoint.h" const cairo_solid_pattern_t _cairo_pattern_nil = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ { 0, 0, 0, NULL }, /* user_data */ @@ -41,7 +41,7 @@ const cairo_solid_pattern_t _cairo_pattern_nil = { }; static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NULL_POINTER, /* status */ { 0, 0, 0, NULL }, /* user_data */ @@ -50,14 +50,16 @@ static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ }; -const cairo_solid_pattern_t _cairo_pattern_none = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ +const cairo_solid_pattern_t _cairo_pattern_black = { + { CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ + CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */ + CAIRO_CONTENT_COLOR, /* content */ }; /** @@ -94,6 +96,23 @@ _cairo_pattern_set_error (cairo_pattern_t *pattern, static void _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) { +#if HAVE_VALGRIND + switch (type) { + case CAIRO_PATTERN_TYPE_SOLID: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); + break; + } +#endif + pattern->type = type; pattern->status = CAIRO_STATUS_SUCCESS; @@ -117,6 +136,9 @@ static cairo_status_t _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, const cairo_gradient_pattern_t *other) { + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; @@ -138,7 +160,7 @@ _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, { pattern->stops = _cairo_malloc_ab (other->stops_size, sizeof (cairo_gradient_stop_t)); - if (pattern->stops == NULL) { + if (unlikely (pattern->stops == NULL)) { pattern->stops_size = 0; pattern->n_stops = 0; return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY); @@ -163,12 +185,16 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t))); + *dst = *src; } break; 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; + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t))); + *dst = *src; cairo_surface_reference (dst->surface); } break; @@ -178,8 +204,14 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; cairo_status_t status; + if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); + } else { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t))); + } + status = _cairo_gradient_pattern_init_copy (dst, src); - if (status) + if (unlikely (status)) return status; } break; @@ -201,7 +233,7 @@ _cairo_pattern_init_snapshot (cairo_pattern_t *pattern, /* We don't bother doing any fancy copy-on-write implementation * for the pattern's data. It's generally quite tiny. */ status = _cairo_pattern_init_copy (pattern, other); - if (status) + if (unlikely (status)) return status; /* But we do let the surface snapshot stuff be as fancy as it @@ -245,12 +277,30 @@ _cairo_pattern_fini (cairo_pattern_t *pattern) free (gradient->stops); } break; } + +#if HAVE_VALGRIND + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t)); + break; + } +#endif } cairo_status_t -_cairo_pattern_create_copy (cairo_pattern_t **pattern, +_cairo_pattern_create_copy (cairo_pattern_t **pattern_out, const cairo_pattern_t *other) { + cairo_pattern_t *pattern; cairo_status_t status; if (other->status) @@ -258,29 +308,32 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern, switch (other->type) { case CAIRO_PATTERN_TYPE_SOLID: - *pattern = malloc (sizeof (cairo_solid_pattern_t)); + pattern = malloc (sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: - *pattern = malloc (sizeof (cairo_surface_pattern_t)); + pattern = malloc (sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: - *pattern = malloc (sizeof (cairo_linear_pattern_t)); + pattern = malloc (sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: - *pattern = malloc (sizeof (cairo_radial_pattern_t)); + pattern = malloc (sizeof (cairo_radial_pattern_t)); break; + default: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); } - if (*pattern == NULL) + if (unlikely (pattern == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_pattern_init_copy (*pattern, other); - if (status) { - free (*pattern); + status = _cairo_pattern_init_copy (pattern, other); + if (unlikely (status)) { + free (pattern); return status; } - CAIRO_REFERENCE_COUNT_INIT (&(*pattern)->ref_count, 1); - + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); + *pattern_out = pattern; return CAIRO_STATUS_SUCCESS; } @@ -355,59 +408,114 @@ _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, /* We use a small freed pattern cache here, because we don't want to * constantly reallocate simple colors. */ #define MAX_PATTERN_CACHE_SIZE 4 -static struct { - cairo_solid_pattern_t *patterns[MAX_PATTERN_CACHE_SIZE]; - int size; -} solid_pattern_cache; +typedef struct { + void *pool[MAX_PATTERN_CACHE_SIZE]; + int top; +} freed_pool_t; -cairo_pattern_t * -_cairo_pattern_create_solid (const cairo_color_t *color, - cairo_content_t content) +static freed_pool_t freed_pattern_pool[4]; + +static void * +_atomic_fetch (void **slot) { - cairo_solid_pattern_t *pattern = NULL; + return _cairo_atomic_ptr_cmpxchg (slot, *slot, NULL); +} - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock); +static cairo_bool_t +_atomic_store (void **slot, void *pattern) +{ + return _cairo_atomic_ptr_cmpxchg (slot, NULL, pattern) == NULL; +} + +static void * +_freed_pattern_get (freed_pool_t *pool) +{ + cairo_pattern_t *pattern; + int i; + + i = pool->top - 1; + if (i < 0) + i = 0; + + pattern = _atomic_fetch (&pool->pool[i]); + if (pattern != NULL) { + pool->top = i; + return pattern; + } - if (solid_pattern_cache.size) { - int i = --solid_pattern_cache.size % - ARRAY_LENGTH (solid_pattern_cache.patterns); - pattern = solid_pattern_cache.patterns[i]; - solid_pattern_cache.patterns[i] = NULL; + /* either empty or contended */ + for (i = ARRAY_LENGTH (pool->pool); i--;) { + pattern = _atomic_fetch (&pool->pool[i]); + if (pattern != NULL) { + pool->top = i; + return pattern; + } } - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock); + /* empty */ + pool->top = 0; + return NULL; +} - if (pattern == NULL) { - /* None cached, need to create a new pattern. */ - pattern = malloc (sizeof (cairo_solid_pattern_t)); +static void +_freed_pattern_put (freed_pool_t *pool, + cairo_pattern_t *pattern) +{ + int i = pool->top; + + if (_atomic_store (&pool->pool[i], pattern)) { + pool->top = i + 1; + return; } - if (pattern == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - pattern = (cairo_solid_pattern_t *) &_cairo_pattern_nil; - } else { - _cairo_pattern_init_solid (pattern, color, content); - CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + /* either full or contended */ + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + if (_atomic_store (&pool->pool[i], pattern)) { + pool->top = i + 1; + return; + } } - return &pattern->base; + /* full */ + pool->top = ARRAY_LENGTH (pool->pool); + free (pattern); } static void -_cairo_pattern_reset_solid_pattern_cache (void) +_freed_patterns_reset (void) { - int i; + int i, j; + + for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) { + freed_pool_t *pool = &freed_pattern_pool[i]; + for (j = 0; j < ARRAY_LENGTH (pool->pool); j++) { + free (pool->pool[j]); + pool->pool[j] = NULL; + } + } +} - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock); +cairo_pattern_t * +_cairo_pattern_create_solid (const cairo_color_t *color, + cairo_content_t content) +{ + cairo_solid_pattern_t *pattern = NULL; - for (i = 0; i < MIN (ARRAY_LENGTH (solid_pattern_cache.patterns), solid_pattern_cache.size); i++) { - if (solid_pattern_cache.patterns[i]) - free (solid_pattern_cache.patterns[i]); - solid_pattern_cache.patterns[i] = NULL; + pattern = + _freed_pattern_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); + if (unlikely (pattern == NULL)) { + /* None cached, need to create a new pattern. */ + pattern = malloc (sizeof (cairo_solid_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil; + } } - solid_pattern_cache.size = 0; - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock); + _cairo_pattern_init_solid (pattern, color, content); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + return &pattern->base; } static const cairo_pattern_t * @@ -452,9 +560,9 @@ cairo_pattern_create_rgb (double red, double green, double blue) { cairo_color_t color; - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); _cairo_color_init_rgb (&color, red, green, blue); @@ -491,10 +599,10 @@ cairo_pattern_create_rgba (double red, double green, double blue, { cairo_color_t color; - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); - _cairo_restrict_value (&alpha, 0.0, 1.0); + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); _cairo_color_init_rgba (&color, red, green, blue, alpha); @@ -532,10 +640,14 @@ cairo_pattern_create_for_surface (cairo_surface_t *surface) if (surface->status) return (cairo_pattern_t*) _cairo_pattern_create_in_error (surface->status); - pattern = malloc (sizeof (cairo_surface_pattern_t)); - if (pattern == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *)&_cairo_pattern_nil.base; + pattern = + _freed_pattern_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); + if (unlikely (pattern == NULL)) { + pattern = malloc (sizeof (cairo_surface_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *)&_cairo_pattern_nil.base; + } } CAIRO_MUTEX_INITIALIZE (); @@ -578,10 +690,14 @@ cairo_pattern_create_linear (double x0, double y0, double x1, double y1) { cairo_linear_pattern_t *pattern; - pattern = malloc (sizeof (cairo_linear_pattern_t)); - if (pattern == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *) &_cairo_pattern_nil.base; + pattern = + _freed_pattern_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); + if (unlikely (pattern == NULL)) { + pattern = malloc (sizeof (cairo_linear_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil.base; + } } CAIRO_MUTEX_INITIALIZE (); @@ -626,10 +742,14 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0, { cairo_radial_pattern_t *pattern; - pattern = malloc (sizeof (cairo_radial_pattern_t)); - if (pattern == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_pattern_t *) &_cairo_pattern_nil.base; + pattern = + _freed_pattern_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); + if (unlikely (pattern == NULL)) { + pattern = malloc (sizeof (cairo_radial_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil.base; + } } CAIRO_MUTEX_INITIALIZE (); @@ -684,7 +804,6 @@ cairo_pattern_get_type (cairo_pattern_t *pattern) { return pattern->type; } -slim_hidden_def (cairo_pattern_get_type); /** * cairo_pattern_status: @@ -714,6 +833,8 @@ slim_hidden_def (cairo_pattern_status); void cairo_pattern_destroy (cairo_pattern_t *pattern) { + cairo_pattern_type_t type; + if (pattern == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return; @@ -723,26 +844,11 @@ cairo_pattern_destroy (cairo_pattern_t *pattern) if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) return; + type = pattern->type; _cairo_pattern_fini (pattern); /* maintain a small cache of freed patterns */ - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - int i; - - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock); - - i = solid_pattern_cache.size++ % - ARRAY_LENGTH (solid_pattern_cache.patterns); - /* swap an old pattern for this 'cache-hot' pattern */ - if (solid_pattern_cache.patterns[i]) - free (solid_pattern_cache.patterns[i]); - - solid_pattern_cache.patterns[i] = (cairo_solid_pattern_t *) pattern; - - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock); - } else { - free (pattern); - } + _freed_pattern_put (&freed_pattern_pool[type], pattern); } slim_hidden_def (cairo_pattern_destroy); @@ -837,6 +943,9 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) return CAIRO_STATUS_SUCCESS; } + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + assert (pattern->n_stops <= pattern->stops_size); if (pattern->stops == pattern->stops_embedded) { @@ -845,11 +954,11 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t)); } else { new_stops = _cairo_realloc_ab (pattern->stops, - new_size, + new_size, sizeof (cairo_gradient_stop_t)); } - if (new_stops == NULL) + if (unlikely (new_stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pattern->stops = new_stops; @@ -871,7 +980,7 @@ _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, if (pattern->n_stops >= pattern->stops_size) { cairo_status_t status = _cairo_pattern_gradient_grow (pattern); - if (status) { + if (unlikely (status)) { status = _cairo_pattern_set_error (&pattern->base, status); return; } @@ -949,10 +1058,10 @@ cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, return; } - _cairo_restrict_value (&offset, 0.0, 1.0); - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); + offset = _cairo_restrict_value (offset, 0.0, 1.0); + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, offset, red, green, blue, 1.0); @@ -1003,11 +1112,11 @@ cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, return; } - _cairo_restrict_value (&offset, 0.0, 1.0); - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); - _cairo_restrict_value (&alpha, 0.0, 1.0); + offset = _cairo_restrict_value (offset, 0.0, 1.0); + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, offset, red, green, blue, alpha); @@ -1055,11 +1164,14 @@ cairo_pattern_set_matrix (cairo_pattern_t *pattern, if (pattern->status) return; + if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) + return; + pattern->matrix = *matrix; inverse = *matrix; status = cairo_matrix_invert (&inverse); - if (status) + if (unlikely (status)) status = _cairo_pattern_set_error (pattern, status); } slim_hidden_def (cairo_pattern_set_matrix); @@ -1229,7 +1341,7 @@ _cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, } static cairo_int_status_t -_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, +_cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pattern, cairo_surface_t *dst, int x, int y, @@ -1243,6 +1355,7 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, pixman_transform_t pixman_transform; cairo_status_t status; cairo_bool_t repeat = FALSE; + cairo_bool_t opaque = TRUE; pixman_gradient_stop_t pixman_stops_static[2]; pixman_gradient_stop_t *pixman_stops = pixman_stops_static; @@ -1250,9 +1363,13 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, int clone_offset_x, clone_offset_y; cairo_matrix_t matrix = pattern->base.matrix; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { - pixman_stops = _cairo_malloc_ab (pattern->n_stops, sizeof(pixman_gradient_stop_t)); - if (pixman_stops == NULL) + pixman_stops = _cairo_malloc_ab (pattern->n_stops, + sizeof(pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1262,6 +1379,8 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, pixman_stops[i].color.green = pattern->stops[i].color.green_short; pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; + if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha)) + opaque = FALSE; } if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) @@ -1336,7 +1455,7 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, if (pixman_stops != pixman_stops_static) free (pixman_stops); - if (pixman_image == NULL) + if (unlikely (pixman_image == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (_cairo_surface_is_image (dst)) @@ -1354,7 +1473,6 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, attr->matrix = matrix; attr->extend = pattern->base.extend; attr->filter = CAIRO_FILTER_NEAREST; - attr->acquired = FALSE; *out = &image->base; @@ -1399,7 +1517,8 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, return image->base.status; } - _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform); + _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform, + width/2., height/2.); if (!pixman_image_set_transform (pixman_image, &pixman_transform)) { cairo_surface_destroy (&image->base); pixman_image_unref (pixman_image); @@ -1432,7 +1551,12 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, pixman_image_unref (pixman_image); + _cairo_debug_check_image_surface_is_defined (&image->base); + status = _cairo_surface_clone_similar (dst, &image->base, + opaque ? + CAIRO_CONTENT_COLOR : + CAIRO_CONTENT_COLOR_ALPHA, 0, 0, width, height, &clone_offset_x, &clone_offset_y, @@ -1445,7 +1569,6 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, cairo_matrix_init_identity (&attr->matrix); attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; attr->filter = CAIRO_FILTER_NEAREST; - attr->acquired = FALSE; return status; } @@ -1493,7 +1616,7 @@ _cairo_pattern_solid_surface_matches_color ( } static cairo_int_status_t -_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, +_cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *pattern, cairo_surface_t *dst, int x, int y, @@ -1516,7 +1639,7 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, dst)) { status = _cairo_surface_reset (solid_surface_cache.cache[i].surface); - if (status) + if (unlikely (status)) goto UNLOCK; goto DONE; @@ -1528,7 +1651,7 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, dst)) { status = _cairo_surface_reset (solid_surface_cache.cache[i].surface); - if (status) + if (unlikely (status)) goto UNLOCK; goto DONE; @@ -1548,11 +1671,11 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, /* Reuse the surface instead of evicting */ status = _cairo_surface_reset (surface); - if (status) + if (unlikely (status)) goto EVICT; status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); - if (status) + if (unlikely (status)) goto EVICT; cairo_surface_reference (surface); @@ -1596,7 +1719,6 @@ NOCACHE: cairo_matrix_init_identity (&attribs->matrix); attribs->extend = CAIRO_EXTEND_REPEAT; attribs->filter = CAIRO_FILTER_NEAREST; - attribs->acquired = FALSE; status = CAIRO_STATUS_SUCCESS; @@ -1624,7 +1746,7 @@ _cairo_pattern_reset_solid_surface_cache (void) solid_surface_cache.cache[solid_surface_cache.size].surface = NULL; /* release the lock to avoid the possibility of a recursive - * deadlock when the scaled font destroy closure gets called */ + * deadlock when the surface destroy closure gets called */ CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); cairo_surface_destroy (surface); CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); @@ -1713,8 +1835,8 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern) * backends do currently (see bug #10508) */ static cairo_filter_t -_cairo_pattern_analyze_filter (cairo_surface_pattern_t *pattern, - double *pad_out) +_cairo_pattern_analyze_filter (const cairo_surface_pattern_t *pattern, + double *pad_out) { double pad; cairo_filter_t optimized_filter; @@ -1764,40 +1886,47 @@ _pixman_nearest_sample (double d) } static cairo_int_status_t -_cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, +_cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern, cairo_surface_t *dst, + cairo_content_t content, int x, int y, unsigned int width, unsigned int height, + unsigned int flags, cairo_surface_t **out, cairo_surface_attributes_t *attr) { - cairo_int_status_t status; + cairo_surface_t *surface; + cairo_rectangle_int_t extents; + cairo_rectangle_int_t sampled_area; + double x1, y1, x2, y2; int tx, ty; double pad; + cairo_bool_t is_identity; + cairo_bool_t is_empty; + cairo_int_status_t status; - attr->acquired = FALSE; + surface = cairo_surface_reference (pattern->surface); + is_identity = FALSE; + attr->matrix = pattern->base.matrix; attr->extend = pattern->base.extend; attr->filter = _cairo_pattern_analyze_filter (pattern, &pad); - if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, - &tx, &ty)) - { + attr->x_offset = attr->y_offset = tx = ty = 0; + if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { cairo_matrix_init_identity (&attr->matrix); attr->x_offset = tx; attr->y_offset = ty; - } - else if (attr->filter == CAIRO_FILTER_NEAREST) - { + is_identity = TRUE; + } else if (attr->filter == CAIRO_FILTER_NEAREST) { /* * For NEAREST, we can remove the fractional translation component * from the transformation - this ensures that the pattern will always * hit fast-paths in the backends for simple transformations that * become (almost) identity, without loss of quality. */ - attr->matrix = pattern->base.matrix; attr->matrix.x0 = 0; attr->matrix.y0 = 0; if (_cairo_matrix_is_pixel_exact (&attr->matrix)) { @@ -1811,15 +1940,12 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, attr->matrix.y0 = pattern->base.matrix.y0; } - attr->x_offset = attr->y_offset = 0; - tx = ty = 0; - } - else - { - attr->matrix = pattern->base.matrix; - attr->x_offset = attr->y_offset = 0; - tx = 0; - ty = 0; + if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { + cairo_matrix_init_identity (&attr->matrix); + attr->x_offset = tx; + attr->y_offset = ty; + is_identity = TRUE; + } } /* XXX: Hack: @@ -1828,156 +1954,204 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, * an image twice bigger on each side, and create a pattern of four * images such that the new image, when repeated, has the same effect * of reflecting the original pattern. - * - * This is because the reflect support in pixman is broken and we - * pass repeat instead of reflect to pixman. See - * _cairo_image_surface_set_attributes() for that. */ - if (attr->extend == CAIRO_EXTEND_REFLECT) { + if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT && + attr->extend == CAIRO_EXTEND_REFLECT) + { cairo_t *cr; - int w,h; + cairo_surface_t *src; + int w, h; - cairo_rectangle_int_t extents; - status = _cairo_surface_get_extents (pattern->surface, &extents); - if (status) - return status; + status = _cairo_surface_get_extents (surface, &extents); + if (unlikely (status)) + goto BAIL; - attr->extend = CAIRO_EXTEND_REPEAT; + status = _cairo_surface_clone_similar (dst, surface, content, + extents.x, extents.y, + extents.width, extents.height, + &extents.x, &extents.y, &src); + if (unlikely (status)) + goto BAIL; - /* TODO: Instead of rendering pattern->surface four times to - * out, we should first copy pattern->surface to surface similar - * to dst and then copy that four times to out. This may cause - * an extra copy in the case of image destination, but for X servers, - * this will send pattern->surface just once over the wire instead - * of current four. - */ - x = extents.x; - y = extents.y; w = 2 * extents.width; h = 2 * extents.height; - *out = cairo_surface_create_similar (dst, dst->content, w, h); - status = cairo_surface_status (*out); - if (status) { - cairo_surface_destroy (*out); - *out = NULL; - return status; + if (is_identity) { + attr->x_offset = -x; + x += tx; + while (x <= -w) + x += w; + while (x >= w) + x -= w; + extents.x += x; + tx = x = 0; + + attr->y_offset = -y; + y += ty; + while (y <= -h) + y += h; + while (y >= h) + y -= h; + extents.y += y; + ty = y = 0; } - (*out)->device_transform = pattern->surface->device_transform; - (*out)->device_transform_inverse = pattern->surface->device_transform_inverse; + cairo_surface_destroy (surface); + surface = cairo_surface_create_similar (dst, dst->content, w, h); + if (surface->status) { + cairo_surface_destroy (src); + return surface->status; + } - cr = cairo_create (*out); + surface->device_transform = pattern->surface->device_transform; + surface->device_transform_inverse = pattern->surface->device_transform_inverse; - cairo_set_source_surface (cr, pattern->surface, -x, -y); + cr = cairo_create (surface); + + cairo_set_source_surface (cr, src, -extents.x, -extents.y); cairo_paint (cr); cairo_scale (cr, -1, +1); - cairo_set_source_surface (cr, pattern->surface, x-w, -y); + cairo_set_source_surface (cr, src, extents.x-w, -extents.y); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x, -extents.y); cairo_paint (cr); cairo_scale (cr, +1, -1); - cairo_set_source_surface (cr, pattern->surface, x-w, y-h); + cairo_set_source_surface (cr, src, extents.x-w, extents.y-h); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x, extents.y-h); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x-w, extents.y); + cairo_paint (cr); + cairo_set_source_surface (cr, src, extents.x, extents.y); cairo_paint (cr); cairo_scale (cr, -1, +1); - cairo_set_source_surface (cr, pattern->surface, -x, y-h); + cairo_set_source_surface (cr, src, -extents.x, extents.y-h); + cairo_paint (cr); + cairo_set_source_surface (cr, src, -extents.x, extents.y); cairo_paint (cr); status = cairo_status (cr); cairo_destroy (cr); - if (status) { - cairo_surface_destroy (*out); - *out = NULL; - } + cairo_surface_destroy (src); - return status; + if (unlikely (status)) + goto BAIL; + + attr->extend = CAIRO_EXTEND_REPEAT; } - if (_cairo_surface_is_image (dst)) { - cairo_image_surface_t *image; + status = _cairo_surface_get_extents (surface, &extents); + if (unlikely (status)) + goto BAIL; - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &attr->extra); - if (status) - return status; + /* We first transform the rectangle to the coordinate space of the + * source surface so that we only need to clone that portion of the + * surface that will be read. + */ + x1 = x; + y1 = y; + x2 = x + (int) width; + y2 = y + (int) height; + if (! is_identity) { + _cairo_matrix_transform_bounding_box (&attr->matrix, + &x1, &y1, &x2, &y2, + NULL); + } - *out = &image->base; - attr->acquired = TRUE; + sampled_area.x = floor (x1 - pad); + sampled_area.y = floor (y1 - pad); + sampled_area.width = ceil (x2 + pad) - sampled_area.x; + sampled_area.height = ceil (y2 + pad) - sampled_area.y; + + sampled_area.x += tx; + sampled_area.y += ty; + + if (attr->extend != CAIRO_EXTEND_REPEAT) { + /* Never acquire a larger area than the source itself */ + is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); } else { - cairo_rectangle_int_t extents; - cairo_bool_t is_empty; + int trim = 0; - status = _cairo_surface_get_extents (pattern->surface, &extents); - if (status) - return status; + if (sampled_area.x >= extents.x && + sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) + { + /* source is horizontally contained within extents, trim */ + extents.x = sampled_area.x; + extents.width = sampled_area.width; + trim |= 0x1; + } - /* If we're repeating, we just play it safe and clone the - * entire surface - i.e. we use the existing extents. - */ - if (attr->extend != CAIRO_EXTEND_REPEAT) { - cairo_rectangle_int_t sampled_area; + if (sampled_area.y >= extents.y && + sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) + { + /* source is vertically contained within extents, trim */ + extents.y = sampled_area.y; + extents.height = sampled_area.height; + trim |= 0x2; + } - /* Otherwise, we first transform the rectangle to the - * coordinate space of the source surface so that we can - * clone only that portion of the surface that will be - * read. - */ - if (_cairo_matrix_is_identity (&attr->matrix)) { - sampled_area.x = x; - sampled_area.y = y; - sampled_area.width = width; - sampled_area.height = height; - } else { - double x1 = x; - double y1 = y; - double x2 = x + (int) width; - double y2 = y + (int) height; + if (trim == 0x3) { + /* source is wholly contained within extents, drop the REPEAT */ + attr->extend = CAIRO_EXTEND_NONE; + } - _cairo_matrix_transform_bounding_box (&attr->matrix, - &x1, &y1, &x2, &y2, - NULL); + is_empty = extents.width == 0 || extents.height == 0; + } - sampled_area.x = floor (x1 - pad); - sampled_area.y = floor (y1 - pad); - sampled_area.width = ceil (x2 + pad) - sampled_area.x; - sampled_area.height = ceil (y2 + pad) - sampled_area.y; + /* XXX can we use is_empty? */ - } + status = _cairo_surface_clone_similar (dst, surface, content, + extents.x, extents.y, + extents.width, extents.height, + &x, &y, out); + if (unlikely (status)) + goto BAIL; + + if (x != 0 || y != 0) { + if (is_identity) { + attr->x_offset -= x; + attr->y_offset -= y; + } else { + cairo_matrix_t m; - sampled_area.x += tx; - sampled_area.y += ty; + x -= attr->x_offset; + y -= attr->y_offset; + attr->x_offset = 0; + attr->y_offset = 0; - /* Never acquire a larger area than the source itself */ - is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); + cairo_matrix_init_translate (&m, -x, -y); + cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m); } + } - /* XXX can we use is_empty? */ - - status = _cairo_surface_clone_similar (dst, pattern->surface, - extents.x, extents.y, - extents.width, extents.height, - &x, &y, out); - if (status == CAIRO_STATUS_SUCCESS && (x != 0 || y != 0)) { - if (_cairo_matrix_is_identity (&attr->matrix)) { - attr->x_offset -= x; - attr->y_offset -= y; - } else { - cairo_matrix_t m; + /* reduce likelihood of range overflow with large downscaling */ + if (! is_identity) { + cairo_matrix_t m; + cairo_status_t invert_status; - x -= attr->x_offset; - y -= attr->y_offset; - attr->x_offset = 0; - attr->y_offset = 0; + m = attr->matrix; + invert_status = cairo_matrix_invert (&m); + assert (invert_status == CAIRO_STATUS_SUCCESS); - cairo_matrix_init_translate (&m, -x, -y); - cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m); - } + if (m.x0 != 0. || m.y0 != 0.) { + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + */ + x = floor (m.x0 / 2); + y = floor (m.y0 / 2); + attr->x_offset -= x; + attr->y_offset -= y; + cairo_matrix_init_translate (&m, x, y); + cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix); } } + BAIL: + cairo_surface_destroy (surface); return status; } @@ -2002,12 +2176,14 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. **/ cairo_int_status_t -_cairo_pattern_acquire_surface (cairo_pattern_t *pattern, +_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_surface_t *dst, + cairo_content_t content, int x, int y, unsigned int width, unsigned int height, + unsigned int flags, cairo_surface_t **surface_out, cairo_surface_attributes_t *attributes) { @@ -2015,7 +2191,6 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, if (pattern->status) { *surface_out = NULL; - attributes->acquired = FALSE; return pattern->status; } @@ -2039,19 +2214,15 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, if (src->n_stops) { - cairo_color_t color; - - _cairo_color_init_rgba (&color, - src->stops->color.red, - src->stops->color.green, - src->stops->color.blue, - src->stops->color.alpha); - - _cairo_pattern_init_solid (&solid, &color, CAIRO_CONTENT_COLOR_ALPHA); + _cairo_pattern_init_solid (&solid, + &src->stops->color, + CAIRO_CONTENT_COLOR_ALPHA); } else { - _cairo_pattern_init_solid (&solid, CAIRO_COLOR_TRANSPARENT, CAIRO_CONTENT_ALPHA); + _cairo_pattern_init_solid (&solid, + CAIRO_COLOR_TRANSPARENT, + CAIRO_CONTENT_ALPHA); } status = _cairo_pattern_acquire_surface_for_solid (&solid, dst, @@ -2062,18 +2233,49 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, } else { - status = _cairo_pattern_acquire_surface_for_gradient (src, dst, - x, y, - width, height, - surface_out, - attributes); + unsigned int i; + + /* Is the gradient a uniform colour? + * Happens more often than you would believe. + */ + for (i = 1; i < src->n_stops; i++) { + if (! _cairo_color_equal (&src->stops[0].color, + &src->stops[i].color)) + { + break; + } + } + if (i == src->n_stops) { + cairo_solid_pattern_t solid; + + _cairo_pattern_init_solid (&solid, + &src->stops->color, + CAIRO_CONTENT_COLOR_ALPHA); + + status = + _cairo_pattern_acquire_surface_for_solid (&solid, dst, + x, y, + width, height, + surface_out, + attributes); + } else { + status = + _cairo_pattern_acquire_surface_for_gradient (src, dst, + x, y, + width, height, + surface_out, + attributes); + } } } break; case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; status = _cairo_pattern_acquire_surface_for_surface (src, dst, - x, y, width, height, + content, + x, y, + width, height, + flags, surface_out, attributes); } break; @@ -2094,44 +2296,32 @@ _cairo_pattern_acquire_surface (cairo_pattern_t *pattern, * Releases resources obtained by _cairo_pattern_acquire_surface. **/ void -_cairo_pattern_release_surface (cairo_pattern_t *pattern, +_cairo_pattern_release_surface (const cairo_pattern_t *pattern, cairo_surface_t *surface, cairo_surface_attributes_t *attributes) { - if (attributes->acquired) - { - cairo_surface_pattern_t *surface_pattern; - - assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); - surface_pattern = (cairo_surface_pattern_t *) pattern; - - _cairo_surface_release_source_image (surface_pattern->surface, - (cairo_image_surface_t *) surface, - attributes->extra); - } - else - { - cairo_surface_destroy (surface); - } + cairo_surface_destroy (surface); } cairo_int_status_t -_cairo_pattern_acquire_surfaces (cairo_pattern_t *src, - cairo_pattern_t *mask, +_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, + const cairo_pattern_t *mask, cairo_surface_t *dst, + cairo_content_t src_content, int src_x, int src_y, int mask_x, int mask_y, unsigned int width, unsigned int height, + unsigned int flags, cairo_surface_t **src_out, cairo_surface_t **mask_out, cairo_surface_attributes_t *src_attributes, cairo_surface_attributes_t *mask_attributes) { cairo_int_status_t status; - cairo_pattern_union_t src_tmp, mask_tmp; + cairo_pattern_union_t src_tmp; if (src->status) return src->status; @@ -2154,50 +2344,39 @@ _cairo_pattern_acquire_surfaces (cairo_pattern_t *src, combined = src_solid->color; _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - _cairo_pattern_init_solid (&src_tmp.solid, &combined, CAIRO_CONTENT_COLOR_ALPHA); + _cairo_pattern_init_solid (&src_tmp.solid, &combined, + (src_solid->content | mask_solid->content) & src_content); + src = &src_tmp.base; mask = NULL; } - else - { - status = _cairo_pattern_init_copy (&src_tmp.base, src); - if (status) - return status; - } - status = _cairo_pattern_acquire_surface (&src_tmp.base, dst, + status = _cairo_pattern_acquire_surface (src, dst, + src_content, src_x, src_y, width, height, + flags, src_out, src_attributes); - if (status) { - _cairo_pattern_fini (&src_tmp.base); - return status; - } + if (unlikely (status)) + goto BAIL; - if (mask == NULL) - { - _cairo_pattern_fini (&src_tmp.base); + if (mask == NULL) { *mask_out = NULL; - return CAIRO_STATUS_SUCCESS; + goto BAIL; } - status = _cairo_pattern_init_copy (&mask_tmp.base, mask); - if (status) - goto CLEANUP_SOURCE; - - status = _cairo_pattern_acquire_surface (&mask_tmp.base, dst, + status = _cairo_pattern_acquire_surface (mask, dst, + CAIRO_CONTENT_ALPHA, mask_x, mask_y, width, height, + flags, mask_out, mask_attributes); + if (unlikely (status)) + _cairo_pattern_release_surface (src, *src_out, src_attributes); - _cairo_pattern_fini (&mask_tmp.base); - -CLEANUP_SOURCE: - if (status) - _cairo_pattern_release_surface (&src_tmp.base, - *src_out, src_attributes); - - _cairo_pattern_fini (&src_tmp.base); + BAIL: + if (src == &src_tmp.base) + _cairo_pattern_fini (&src_tmp.base); return status; } @@ -2215,23 +2394,25 @@ CLEANUP_SOURCE: * with a little more work. **/ cairo_status_t -_cairo_pattern_get_extents (cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents) +_cairo_pattern_get_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents) { if (pattern->extend == CAIRO_EXTEND_NONE && pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_status_t status; cairo_rectangle_int_t surface_extents; - cairo_surface_pattern_t *surface_pattern = - (cairo_surface_pattern_t *) pattern; + const cairo_surface_pattern_t *surface_pattern = + (const cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; cairo_matrix_t imatrix; double x1, y1, x2, y2; double pad; status = _cairo_surface_get_extents (surface, &surface_extents); - if (status) + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + goto UNBOUNDED; + if (unlikely (status)) return status; /* The filter can effectively enlarge the extents of the @@ -2277,6 +2458,7 @@ _cairo_pattern_get_extents (cairo_pattern_t *pattern, * horizontal/vertical linear gradients). */ + UNBOUNDED: /* unbounded patterns -> 'infinite' extents */ extents->x = CAIRO_RECT_INT_MIN; extents->y = CAIRO_RECT_INT_MIN; @@ -2286,6 +2468,266 @@ _cairo_pattern_get_extents (cairo_pattern_t *pattern, return CAIRO_STATUS_SUCCESS; } + +static unsigned long +_cairo_solid_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &solid->content, sizeof (solid->content)); + hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); + + return hash; +} + +static unsigned long +_cairo_gradient_color_stops_hash (unsigned long hash, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int n; + + hash = _cairo_hash_bytes (hash, + &gradient->n_stops, + sizeof (gradient->n_stops)); + + for (n = 0; n < gradient->n_stops; n++) { + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].offset, + sizeof (double)); + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].color, + sizeof (cairo_color_t)); + } + + return hash; +} + +static unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1)); + hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2)); + + return _cairo_gradient_color_stops_hash (hash, &linear->base); +} + +static unsigned long +_cairo_radial_pattern_hash (unsigned long hash, const cairo_pattern_t *pattern) +{ + const cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1)); + hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1)); + hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2)); + hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2)); + + return _cairo_gradient_color_stops_hash (hash, &radial->base); +} + +static unsigned long +_cairo_surface_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; + + hash ^= surface->surface->unique_id; + + return hash; +} + +unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + if (pattern->status) + return 0; + + hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); + hash = _cairo_hash_bytes (hash, &pattern->matrix, sizeof (pattern->matrix)); + hash = _cairo_hash_bytes (hash, &pattern->filter, sizeof (pattern->filter)); + hash = _cairo_hash_bytes (hash, &pattern->extend, sizeof (pattern->extend)); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_linear_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_radial_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_pattern_hash (hash, pattern); + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +static unsigned long +_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + + return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t)); +} + +unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern) +{ + if (pattern->status) + return 0; + + /* XXX */ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return sizeof (cairo_solid_pattern_t); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + return sizeof (cairo_surface_pattern_t); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + return sizeof (cairo_linear_pattern_t) + + _cairo_gradient_pattern_color_stops_size (pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + return sizeof (cairo_radial_pattern_t) + + _cairo_gradient_pattern_color_stops_size (pattern); + default: + ASSERT_NOT_REACHED; + return 0; + } +} + + +static cairo_bool_t +_cairo_solid_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A; + const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B; + + if (a->content != b->content) + return FALSE; + + return _cairo_color_equal (&a->color, &b->color); +} + +static cairo_bool_t +_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, + const cairo_gradient_pattern_t *b) +{ + unsigned int n; + + if (a->n_stops != b->n_stops) + return FALSE; + + for (n = 0; n < a->n_stops; n++) { + if (a->stops[n].offset != b->stops[n].offset) + return FALSE; + if (! _cairo_color_equal (&a->stops[n].color, &b->stops[n].color)) + return FALSE; + } + + return TRUE; +} + +static cairo_bool_t +_cairo_linear_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_linear_pattern_t *a = (cairo_linear_pattern_t *) A; + const cairo_linear_pattern_t *b = (cairo_linear_pattern_t *) B; + + if (a->p1.x != b->p1.x) + return FALSE; + + if (a->p1.y != b->p1.y) + return FALSE; + + if (a->p2.x != b->p2.x) + return FALSE; + + if (a->p2.y != b->p2.y) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +static cairo_bool_t +_cairo_radial_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_radial_pattern_t *a = (cairo_radial_pattern_t *) A; + const cairo_radial_pattern_t *b = (cairo_radial_pattern_t *) B; + + if (a->c1.x != b->c1.x) + return FALSE; + + if (a->c1.y != b->c1.y) + return FALSE; + + if (a->r1 != b->r1) + return FALSE; + + if (a->c2.x != b->c2.x) + return FALSE; + + if (a->c2.y != b->c2.y) + return FALSE; + + if (a->r2 != b->r2) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +static cairo_bool_t +_cairo_surface_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A; + const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B; + + return a->surface->unique_id == b->surface->unique_id; +} + +cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) +{ + if (a->status || b->status) + return FALSE; + + if (a->type != b->type) + return FALSE; + + if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) + return FALSE; + + if (a->filter != b->filter) + return FALSE; + + if (a->extend != b->extend) + return FALSE; + + switch (a->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_linear_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_radial_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_pattern_equal (a, b); + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + /** * cairo_pattern_get_rgba * @pattern: a #cairo_pattern_t @@ -2310,6 +2752,9 @@ cairo_pattern_get_rgba (cairo_pattern_t *pattern, cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; double r0, g0, b0, a0; + if (pattern->status) + return pattern->status; + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); @@ -2348,6 +2793,9 @@ cairo_pattern_get_surface (cairo_pattern_t *pattern, { cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern; + if (pattern->status) + return pattern->status; + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); @@ -2386,6 +2834,9 @@ cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; + if (pattern->status) + return pattern->status; + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); @@ -2427,6 +2878,9 @@ cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; + if (pattern->status) + return pattern->status; + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); @@ -2460,6 +2914,9 @@ cairo_pattern_get_linear_points (cairo_pattern_t *pattern, { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern; + if (pattern->status) + return pattern->status; + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); @@ -2501,6 +2958,9 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, { cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern; + if (pattern->status) + return pattern->status; + if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); @@ -2523,6 +2983,6 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, void _cairo_pattern_reset_static_data (void) { - _cairo_pattern_reset_solid_pattern_cache (); + _freed_patterns_reset (); _cairo_pattern_reset_solid_surface_cache (); } diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h index 7ff843b..4ef0fca 100644 --- a/src/cairo-pdf-operators-private.h +++ b/src/cairo-pdf-operators-private.h @@ -68,6 +68,7 @@ typedef struct _cairo_pdf_operators { cairo_scaled_font_subsets_t *font_subsets; cairo_pdf_operators_use_font_subset_t use_font_subset; void *use_font_subset_closure; + cairo_bool_t use_actual_text; cairo_bool_t in_text_object; /* inside BT/ET pair */ /* PDF text state */ @@ -115,6 +116,10 @@ cairo_private void _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, cairo_matrix_t *cairo_to_pdf); +cairo_private void +_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, + cairo_bool_t enable); + cairo_private cairo_status_t _cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators); diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index 819318a..22c0a88 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -68,6 +68,7 @@ _cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, pdf_operators->in_text_object = FALSE; pdf_operators->num_glyphs = 0; pdf_operators->has_line_style = FALSE; + pdf_operators->use_actual_text = FALSE; } cairo_status_t @@ -105,6 +106,13 @@ _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operato pdf_operators->has_line_style = FALSE; } +cairo_private void +_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, + cairo_bool_t enable) +{ + pdf_operators->use_actual_text = enable; +} + /* Finish writing out any pending commands to the stream. This * function must be called by the surface before emitting anything * into the PDF stream. @@ -112,7 +120,7 @@ _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operato * pdf_operators may leave the emitted PDF for some operations * unfinished in case subsequent operations can be merged. This * function will finish off any incomplete operation so the stream - * will be in a state where the surface may emit it's own PDF + * will be in a state where the surface may emit its own PDF * operations (eg changing patterns). * */ @@ -293,13 +301,14 @@ _word_wrap_stream_create (cairo_output_stream_t *output, int max_column) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (word_wrap_stream_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _word_wrap_stream_write, + NULL, _word_wrap_stream_close); stream->output = output; stream->max_column = max_column; @@ -320,7 +329,8 @@ typedef struct _pdf_path_info { } pdf_path_info_t; static cairo_status_t -_cairo_pdf_path_move_to (void *closure, cairo_point_t *point) +_cairo_pdf_path_move_to (void *closure, + const cairo_point_t *point) { pdf_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); @@ -336,7 +346,8 @@ _cairo_pdf_path_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cairo_pdf_path_line_to (void *closure, cairo_point_t *point) +_cairo_pdf_path_line_to (void *closure, + const cairo_point_t *point) { pdf_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); @@ -360,9 +371,9 @@ _cairo_pdf_path_line_to (void *closure, cairo_point_t *point) static cairo_status_t _cairo_pdf_path_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) { pdf_path_info_t *info = closure; double bx = _cairo_fixed_to_double (b->x); @@ -438,7 +449,7 @@ _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72); status = _cairo_output_stream_get_status (word_wrap); - if (status) + if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap); info.output = word_wrap; @@ -479,7 +490,7 @@ _cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, path, &pdf_operators->cairo_to_pdf, CAIRO_LINE_CAP_ROUND); - if (status) + if (unlikely (status)) return status; } @@ -560,7 +571,7 @@ _cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, */ if (num_dashes % 2) { dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); - if (dash == NULL) + if (unlikely (dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dash, style->dash, num_dashes * sizeof (double)); @@ -576,7 +587,7 @@ _cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, */ if (dash == style->dash) { dash = _cairo_malloc_ab (num_dashes, sizeof (double)); - if (dash == NULL) + if (unlikely (dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dash, style->dash, num_dashes * sizeof (double)); } @@ -705,7 +716,7 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, if (pdf_operators->in_text_object) { status = _cairo_pdf_operators_end_text (pdf_operators); - if (status) + if (unlikely (status)) return status; } @@ -744,7 +755,7 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, _cairo_matrix_factor_out_scale (&m, &scale); path_transform = m; status = cairo_matrix_invert (&path_transform); - if (status) + if (unlikely (status)) return status; cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf); @@ -753,7 +764,7 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; if (has_ctm) { @@ -769,7 +780,7 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, path, &path_transform, style->line_cap); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator); @@ -806,7 +817,7 @@ _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, if (pdf_operators->in_text_object) { status = _cairo_pdf_operators_end_text (pdf_operators); - if (status) + if (unlikely (status)) return status; } @@ -814,7 +825,7 @@ _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, path, &pdf_operators->cairo_to_pdf, CAIRO_LINE_CAP_ROUND); - if (status) + if (unlikely (status)) return status; switch (fill_rule) { @@ -955,7 +966,7 @@ _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72); status = _cairo_output_stream_get_status (word_wrap_stream); - if (status) + if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap_stream); /* Check if glyph advance used to position every glyph */ @@ -1014,7 +1025,7 @@ _cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, /* We require the matrix to be invertable. */ inverse = *matrix; status = cairo_matrix_invert (&inverse); - if (status) + if (unlikely (status)) return status; pdf_operators->text_matrix = *matrix; @@ -1103,7 +1114,7 @@ _cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_ope status = pdf_operators->use_font_subset (subset_glyph->font_id, subset_glyph->subset_id, pdf_operators->use_font_subset_closure); - if (status) + if (unlikely (status)) return status; } pdf_operators->font_id = subset_glyph->font_id; @@ -1134,7 +1145,7 @@ _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) cairo_status_t status; status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); @@ -1168,7 +1179,7 @@ _cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText <feff"); if (utf8_len) { status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len); - if (status) + if (unlikely (status)) return status; for (i = 0; i < utf16_len; i++) { @@ -1203,11 +1214,11 @@ _cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operator pdf_operators->subset_id != subset_glyph->subset_id) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); - if (status) + if (unlikely (status)) return status; pdf_operators->is_new_text_object = FALSE; @@ -1232,14 +1243,14 @@ _cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operator fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (status) + if (unlikely (status)) return status; x = glyph->x; y = glyph->y; cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); - if (status) + if (unlikely (status)) return status; x = 0.0; @@ -1287,14 +1298,14 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, utf8, utf8_len, &subset_glyph); - if (status) + if (unlikely (status)) return status; if (subset_glyph.utf8_is_mapped || utf8_len < 0) { status = _cairo_pdf_operators_emit_glyph (pdf_operators, glyphs, &subset_glyph); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1304,11 +1315,11 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, /* Fallback to using ActualText to map zero or more glyphs to a * unicode string. */ status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); - if (status) + if (unlikely (status)) return status; cur_glyph = glyphs; @@ -1320,13 +1331,13 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, cur_glyph->index, NULL, -1, &subset_glyph); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_emit_glyph (pdf_operators, cur_glyph, &subset_glyph); - if (status) + if (unlikely (status)) return status; if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) @@ -1335,7 +1346,7 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, cur_glyph++; } status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_end_actualtext (pdf_operators); @@ -1365,13 +1376,13 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); if (status == CAIRO_STATUS_INVALID_MATRIX) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; pdf_operators->is_new_text_object = FALSE; if (pdf_operators->in_text_object == FALSE) { status = _cairo_pdf_operators_begin_text (pdf_operators); - if (status) + if (unlikely (status)) return status; /* Force Tm and Tf to be emitted when starting a new text @@ -1392,7 +1403,7 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (status) + if (unlikely (status)) return status; x = glyphs[0].x; @@ -1403,7 +1414,7 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix); if (status == CAIRO_STATUS_INVALID_MATRIX) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; } @@ -1423,7 +1434,7 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, clusters[i].num_glyphs, cluster_flags, scaled_font); - if (status) + if (unlikely (status)) return status; cur_text += clusters[i].num_bytes; @@ -1439,7 +1450,7 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, 1, FALSE, scaled_font); - if (status) + if (unlikely (status)) return status; } } diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index ab222de..e64b78a 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -60,12 +60,19 @@ typedef struct _cairo_pdf_group_resources { cairo_array_t fonts; } cairo_pdf_group_resources_t; +typedef struct _cairo_pdf_pattern_entry { + cairo_hash_entry_t base; + unsigned int id; + cairo_pdf_resource_t pattern_res; + cairo_pdf_resource_t gstate_res; +} cairo_pdf_pattern_entry_t; + typedef struct _cairo_pdf_pattern { double width; double height; + cairo_rectangle_int_t extents; cairo_pattern_t *pattern; - cairo_pdf_resource_t pattern_res; - cairo_pdf_resource_t gstate_res; + cairo_pdf_pattern_entry_t *hash_entry; } cairo_pdf_pattern_t; typedef enum _cairo_pdf_operation { @@ -117,7 +124,8 @@ struct _cairo_pdf_surface { cairo_array_t pages; cairo_array_t rgb_linear_functions; cairo_array_t alpha_linear_functions; - cairo_array_t patterns; + cairo_array_t page_patterns; + cairo_hash_table_t *all_patterns; cairo_array_t smask_groups; cairo_array_t knockout_group; @@ -127,12 +135,14 @@ struct _cairo_pdf_surface { cairo_pdf_resource_t next_available_resource; cairo_pdf_resource_t pages_resource; + cairo_pdf_version_t pdf_version; cairo_bool_t compress_content; cairo_pdf_resource_t content; cairo_pdf_resource_t content_resources; cairo_pdf_group_resources_t resources; cairo_bool_t has_fallback_images; + cairo_bool_t header_emitted; struct { cairo_bool_t active; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 44f0ed8..2918523 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -45,6 +45,7 @@ #include "cairo-pdf-surface-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-analysis-surface-private.h" +#include "cairo-image-info-private.h" #include "cairo-meta-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-paginated-private.h" @@ -110,6 +111,20 @@ * XObject instead of using an indirect object. */ +static const cairo_pdf_version_t _cairo_pdf_versions[] = +{ + CAIRO_PDF_VERSION_1_4, + CAIRO_PDF_VERSION_1_5 +}; + +#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions) + +static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] = +{ + "PDF 1.4", + "PDF 1.5" +}; + typedef struct _cairo_pdf_object { long offset; } cairo_pdf_object_t; @@ -179,6 +194,9 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); static cairo_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); +static cairo_bool_t +_cairo_pdf_pattern_equal (const void *key_a, const void *key_b); + static const cairo_surface_backend_t cairo_pdf_surface_backend; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; @@ -192,7 +210,7 @@ _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) object.offset = _cairo_output_stream_get_position (surface->output); status = _cairo_array_append (&surface->objects, &object); - if (status) { + if (unlikely (status)) { resource.id = 0; return resource; } @@ -234,7 +252,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, cairo_status_t status, status_ignored; surface = malloc (sizeof (cairo_pdf_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { /* destroy stream on behalf of caller */ status = _cairo_output_stream_destroy (output); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -253,25 +271,32 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t)); _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t)); _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); - _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_pattern_t)); _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *)); _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); + surface->all_patterns = _cairo_hash_table_create (_cairo_pdf_pattern_equal); + if (unlikely (surface->all_patterns == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL0; + } + _cairo_pdf_group_resources_init (&surface->resources); surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); if (! surface->font_subsets) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL0; + goto BAIL1; } surface->next_available_resource.id = 1; surface->pages_resource = _cairo_pdf_surface_new_object (surface); if (surface->pages_resource.id == 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL1; + goto BAIL2; } + surface->pdf_version = CAIRO_PDF_VERSION_1_5; surface->compress_content = TRUE; surface->pdf_stream.active = FALSE; surface->pdf_stream.old_output = NULL; @@ -284,6 +309,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->force_fallbacks = FALSE; surface->select_pattern_gstate_saved = FALSE; surface->current_pattern_is_solid_color = FALSE; + surface->header_emitted = FALSE; _cairo_pdf_operators_init (&surface->pdf_operators, surface->output, @@ -292,12 +318,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, _cairo_pdf_surface_add_font, surface); - - /* Document header */ - _cairo_output_stream_printf (surface->output, - "%%PDF-1.4\n"); - _cairo_output_stream_printf (surface->output, - "%%%c%c%c%c\n", 181, 237, 174, 251); + _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE); surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, @@ -312,8 +333,10 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, return surface->paginated_surface; } -BAIL1: +BAIL2: _cairo_scaled_font_subsets_destroy (surface->font_subsets); +BAIL1: + _cairo_hash_table_destroy (surface->all_patterns); BAIL0: _cairo_array_fini (&surface->objects); free (surface); @@ -437,6 +460,83 @@ _extract_pdf_surface (cairo_surface_t *surface, } /** + * cairo_pdf_surface_restrict_to_version: + * @surface: a PDF #cairo_surface_t + * @version: PDF version + * + * Restricts the generated PDF file to @version. See cairo_pdf_get_versions() + * for a list of available version values that can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.10 + **/ +void +cairo_pdf_surface_restrict_to_version (cairo_surface_t *abstract_surface, + cairo_pdf_version_t version) +{ + cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */ + cairo_status_t status; + + status = _extract_pdf_surface (abstract_surface, &surface); + if (status) { + status = _cairo_surface_set_error (abstract_surface, status); + return; + } + + if (version < CAIRO_PDF_VERSION_LAST) + surface->pdf_version = version; + + _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, + version >= CAIRO_PDF_VERSION_1_5); +} + +/** + * cairo_pdf_get_versions: + * @versions: supported version list + * @num_versions: list length + * + * Used to retrieve the list of supported versions. See + * cairo_pdf_surface_restrict_to_version(). + * + * Since: 1.10 + **/ +void +cairo_pdf_get_versions (cairo_pdf_version_t const **versions, + int *num_versions) +{ + if (versions != NULL) + *versions = _cairo_pdf_versions; + + if (num_versions != NULL) + *num_versions = CAIRO_PDF_VERSION_LAST; +} + +/** + * cairo_pdf_version_to_string: + * @version: a version id + * + * Get the string representation of the given @version id. This function + * will return %NULL if @version isn't valid. See cairo_pdf_get_versions() + * for a way to get the list of valid version ids. + * + * Return value: the string associated to given version. + * + * Since: 1.10 + **/ +const char * +cairo_pdf_version_to_string (cairo_pdf_version_t version) +{ + if (version >= CAIRO_PDF_VERSION_LAST) + return NULL; + + return _cairo_pdf_version_strings[version]; +} + +/** * cairo_pdf_surface_set_size: * @surface: a PDF #cairo_surface_t * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) @@ -462,7 +562,7 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, cairo_status_t status; status = _extract_pdf_surface (surface, &pdf_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -473,7 +573,7 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface, width_in_points, height_in_points); - if (status) + if (unlikely (status)) status = _cairo_surface_set_error (surface, status); } @@ -484,12 +584,12 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) cairo_pdf_pattern_t *pattern; cairo_pdf_smask_group_t *group; - size = _cairo_array_num_elements (&surface->patterns); + size = _cairo_array_num_elements (&surface->page_patterns); for (i = 0; i < size; i++) { - pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->patterns, i); + pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i); cairo_pattern_destroy (pattern->pattern); } - _cairo_array_truncate (&surface->patterns, 0); + _cairo_array_truncate (&surface->page_patterns, 0); size = _cairo_array_num_elements (&surface->smask_groups); for (i = 0; i < size; i++) { @@ -550,7 +650,7 @@ _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, } status = _cairo_array_append (&res->alphas, &alpha); - if (status) + if (unlikely (status)) return status; *index = _cairo_array_num_elements (&res->alphas) - 1; @@ -613,7 +713,7 @@ _cairo_pdf_surface_add_font (unsigned int font_id, return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_array_append (&surface->fonts, &font); - if (status) + if (unlikely (status)) return status; return _cairo_array_append (&res->fonts, &font); @@ -728,7 +828,7 @@ _cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) cairo_pdf_smask_group_t *group; group = calloc (1, sizeof (cairo_pdf_smask_group_t)); - if (group == NULL) { + if (unlikely (group == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } @@ -772,13 +872,31 @@ _cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, return _cairo_array_append (&surface->smask_groups, &group); } +static cairo_bool_t +_cairo_pdf_pattern_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_pattern_entry_t *a = key_a; + const cairo_pdf_pattern_entry_t *b = key_b; + + return a->id == b->id; +} + +static void +_cairo_pdf_pattern_init_key (cairo_pdf_pattern_entry_t *key) +{ + key->base.hash = key->id; +} + static cairo_status_t _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { cairo_pdf_pattern_t pdf_pattern; + cairo_pdf_pattern_entry_t pdf_pattern_key; + cairo_pdf_pattern_entry_t *pdf_pattern_entry; cairo_status_t status; /* Solid colors are emitted into the content stream */ @@ -788,6 +906,7 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } + pdf_pattern_key.id = 0; if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { @@ -807,22 +926,51 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, } } - pdf_pattern.pattern = cairo_pattern_reference (pattern); - pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface); - if (pdf_pattern.pattern_res.id == 0) { - cairo_pattern_destroy (pattern); + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; + + pdf_pattern_key.id = surface->surface->unique_id; + } + + _cairo_pdf_pattern_init_key (&pdf_pattern_key); + if (pdf_pattern_key.base.hash != 0 && + (pdf_pattern_entry = _cairo_hash_table_lookup (surface->all_patterns, + &pdf_pattern_key.base))) + { + *pattern_res = pdf_pattern_entry->pattern_res; + *gstate_res = pdf_pattern_entry->gstate_res; + + return CAIRO_STATUS_SUCCESS; + } + + pdf_pattern_entry = malloc (sizeof (cairo_pdf_pattern_entry_t)); + if (pdf_pattern_entry == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pdf_pattern_entry->id = pdf_pattern_key.id; + _cairo_pdf_pattern_init_key (pdf_pattern_entry); + + pdf_pattern.hash_entry = pdf_pattern_entry; + + status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); + if (unlikely (status)) + return status; + + pdf_pattern_entry->pattern_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern_entry->pattern_res.id == 0) { + cairo_pattern_destroy (pdf_pattern.pattern); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - pdf_pattern.gstate_res.id = 0; + pdf_pattern_entry->gstate_res.id = 0; /* gradient patterns require an smask object to implement transparency */ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { if (_cairo_pattern_is_opaque (pattern) == FALSE) { - pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); - if (pdf_pattern.gstate_res.id == 0) { - cairo_pattern_destroy (pattern); + pdf_pattern_entry->gstate_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern_entry->gstate_res.id == 0) { + cairo_pattern_destroy (pdf_pattern.pattern); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } @@ -830,15 +978,31 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, pdf_pattern.width = surface->width; pdf_pattern.height = surface->height; - *pattern_res = pdf_pattern.pattern_res; - *gstate_res = pdf_pattern.gstate_res; + if (extents) { + pdf_pattern.extents = *extents; + } else { + pdf_pattern.extents.x = 0; + pdf_pattern.extents.y = 0; + pdf_pattern.extents.width = surface->width; + pdf_pattern.extents.height = surface->height; + } - status = _cairo_array_append (&surface->patterns, &pdf_pattern); - if (status) { - cairo_pattern_destroy (pattern); + *pattern_res = pdf_pattern_entry->pattern_res; + *gstate_res = pdf_pattern_entry->gstate_res; + + status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); + if (unlikely (status)) { + cairo_pattern_destroy (pdf_pattern.pattern); return status; } + if (pdf_pattern_entry->base.hash != 0) { + status = _cairo_hash_table_insert (surface->all_patterns, + &pdf_pattern_entry->base); + if (unlikely (status)) + return status; + } + return CAIRO_STATUS_SUCCESS; } @@ -913,18 +1077,21 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, static cairo_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_status_t status; long length; if (! surface->pdf_stream.active) return CAIRO_STATUS_SUCCESS; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) - return status; if (surface->pdf_stream.compressed) { - status = _cairo_output_stream_destroy (surface->output); + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (surface->output); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = status2; + surface->output = surface->pdf_stream.old_output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); surface->pdf_stream.old_output = NULL; @@ -949,7 +1116,7 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) surface->pdf_stream.active = FALSE; - if (status == CAIRO_STATUS_SUCCESS) + if (likely (status == CAIRO_STATUS_SUCCESS)) status = _cairo_output_stream_get_status (surface->output); return status; @@ -1049,7 +1216,7 @@ _cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface) cairo_status_t status; status = _cairo_pdf_surface_open_group (surface, NULL); - if (status) + if (unlikely (status)) return status; surface->group_stream.is_knockout = TRUE; @@ -1067,7 +1234,7 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, assert (surface->group_stream.active == TRUE); status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; if (surface->compress_content) { @@ -1135,7 +1302,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, surface->compress_content, NULL); } - if (status) + if (unlikely (status)) return status; surface->content = surface->pdf_stream.self; @@ -1154,12 +1321,12 @@ _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) assert (surface->group_stream.active == FALSE); status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); status = _cairo_pdf_surface_close_stream (surface); - if (status) + if (unlikely (status)) return status; _cairo_pdf_surface_update_object (surface, surface->content_resources); @@ -1182,6 +1349,16 @@ _cairo_pdf_surface_create_similar (void *abstract_surface, return _cairo_meta_surface_create (content, width, height); } +static void +_cairo_pdf_pattern_entry_pluck (void *entry, void *closure) +{ + cairo_pdf_pattern_entry_t *pattern_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &pattern_entry->base); + free (pattern_entry); +} + static cairo_status_t _cairo_pdf_surface_finish (void *abstract_surface) { @@ -1222,10 +1399,12 @@ _cairo_pdf_surface_finish (void *abstract_surface) "%%%%EOF\n", offset); - status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); /* pdf_operators has already been flushed when the last stream was - * closed so we should never be writing anything here. */ - assert(status2 == CAIRO_STATUS_SUCCESS); + * closed so we should never be writing anything here - however, + * the stream may itself be in an error state. */ + status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; /* close any active streams still open due to fatal errors */ status2 = _cairo_pdf_surface_close_stream (surface); @@ -1259,7 +1438,11 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->pages); _cairo_array_fini (&surface->rgb_linear_functions); _cairo_array_fini (&surface->alpha_linear_functions); - _cairo_array_fini (&surface->patterns); + _cairo_array_fini (&surface->page_patterns); + _cairo_hash_table_foreach (surface->all_patterns, + _cairo_pdf_pattern_entry_pluck, + surface->all_patterns); + _cairo_hash_table_destroy (surface->all_patterns); _cairo_array_fini (&surface->smask_groups); _cairo_array_fini (&surface->fonts); _cairo_array_fini (&surface->knockout_group); @@ -1277,6 +1460,27 @@ _cairo_pdf_surface_start_page (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; + /* Document header */ + if (! surface->header_emitted) { + const char *version; + + switch (surface->pdf_version) { + case CAIRO_PDF_VERSION_1_4: + version = "1.4"; + break; + default: + case CAIRO_PDF_VERSION_1_5: + version = "1.5"; + break; + } + + _cairo_output_stream_printf (surface->output, + "%%PDF-%s\n", version); + _cairo_output_stream_printf (surface->output, + "%%%c%c%c%c\n", 181, 237, 174, 251); + surface->header_emitted = TRUE; + } + _cairo_pdf_group_resources_clear (&surface->resources); return CAIRO_STATUS_SUCCESS; @@ -1291,7 +1495,7 @@ _cairo_pdf_surface_has_fallback_images (void *abstract_surface, surface->has_fallback_images = has_fallbacks; status = _cairo_pdf_surface_open_content_stream (surface, has_fallbacks); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1338,7 +1542,7 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, alpha = _cairo_malloc_ab (image->height, image->width); } - if (alpha == NULL) { + if (unlikely (alpha == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } @@ -1392,7 +1596,7 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, " /BitsPerComponent %d\n", image->width, image->height, image->format == CAIRO_FORMAT_A1 ? 1 : 8); - if (status) + if (unlikely (status)) goto CLEANUP_ALPHA; *stream_ret = surface->pdf_stream.self; @@ -1408,9 +1612,10 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, /* Emit image data into the given surface, providing a resource that * can be used to reference the data in image_ret. */ static cairo_status_t -_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image, - cairo_pdf_resource_t *image_ret) +_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image, + cairo_pdf_resource_t *image_ret, + cairo_filter_t filter) { cairo_status_t status = CAIRO_STATUS_SUCCESS; char *rgb; @@ -1419,6 +1624,7 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, int i, x, y; cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ cairo_bool_t need_smask; + const char *interpolate = "true"; /* These are the only image formats we currently support, (which * makes things a lot simpler here). This is enforced through @@ -1432,7 +1638,7 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, rgb_size = image->height * image->width * 3; rgb = _cairo_malloc_abc (image->width, image->height, 3); - if (rgb == NULL) { + if (unlikely (rgb == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } @@ -1476,18 +1682,32 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1) { status = _cairo_pdf_surface_emit_smask (surface, image, &smask); - if (status) + if (unlikely (status)) goto CLEANUP_RGB; if (smask.id) need_smask = TRUE; } + switch (filter) { + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + #define IMAGE_DICTIONARY " /Type /XObject\n" \ " /Subtype /Image\n" \ " /Width %d\n" \ " /Height %d\n" \ " /ColorSpace /DeviceRGB\n" \ + " /Interpolate %s\n" \ " /BitsPerComponent 8\n" if (need_smask) @@ -1497,14 +1717,16 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, IMAGE_DICTIONARY " /SMask %d 0 R\n", image->width, image->height, + interpolate, smask.id); else status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, IMAGE_DICTIONARY, - image->width, image->height); - if (status) + image->width, image->height, + interpolate); + if (unlikely (status)) goto CLEANUP_RGB; #undef IMAGE_DICTIONARY @@ -1519,29 +1741,192 @@ CLEANUP: return status; } +static cairo_int_status_t +_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_resource_t *res, + int *width, + int *height) +{ + cairo_status_t status; + const unsigned char *mime_data; + unsigned int mime_data_length; + cairo_image_info_t info; + + if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length); + if (status) + return status; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceRGB\n" + " /Filter /JPXDecode\n", + info.width, + info.height); + if (status) + return status; + + *res = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, mime_data, mime_data_length); + _cairo_output_stream_printf (surface->output, "\n"); + status = _cairo_pdf_surface_close_stream (surface); + + *width = info.width; + *height = info.height; + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_resource_t *res, + int *width, + int *height) +{ + cairo_status_t status; + const unsigned char *mime_data; + unsigned int mime_data_length; + cairo_image_info_t info; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.num_components != 1 && info.num_components != 3) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace %s\n" + " /BitsPerComponent %d\n" + " /Filter /DCTDecode\n", + info.width, + info.height, + info.num_components == 1 ? "/DeviceGray" : "/DeviceRGB", + info.bits_per_component); + if (unlikely (status)) + return status; + + *res = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, mime_data, mime_data_length); + _cairo_output_stream_printf (surface->output, "\n"); + status = _cairo_pdf_surface_close_stream (surface); + + *width = info.width; + *height = info.height; + + return status; +} + static cairo_status_t _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, - cairo_surface_pattern_t *pattern, + cairo_pdf_pattern_t *pdf_pattern, cairo_pdf_resource_t *resource, int *width, - int *height) + int *height, + int *origin_x, + int *origin_y) { cairo_image_surface_t *image; + cairo_surface_t *pad_image; void *image_extra; cairo_status_t status; + cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; + int x = 0; + int y = 0; + + status = _cairo_pdf_surface_emit_jpx_image (surface, pattern->surface, + resource, width, height); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_pdf_surface_emit_jpeg_image (surface, pattern->surface, + resource, width, height); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (status) - goto BAIL; + if (unlikely (status)) + return status; - status = _cairo_pdf_surface_emit_image (surface, image, resource); - if (status) + pad_image = &image->base; + if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_pattern_t pad_pattern; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, &pdf_pattern->extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + x = -rect.x; + y = -rect.y; + + pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, + rect.width, + rect.height); + if (pad_image->status) { + status = pad_image->status; + goto BAIL; + } + + _cairo_pattern_init_for_surface (&pad_pattern, &image->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &pad_pattern.base, + NULL, + pad_image, + 0, 0, + 0, 0, + 0, 0, + rect.width, + rect.height); + _cairo_pattern_fini (&pad_pattern.base); + if (unlikely (status)) + goto BAIL; + } + + status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image, + resource, pattern->base.filter); + if (unlikely (status)) goto BAIL; - *width = image->width; - *height = image->height; + *width = ((cairo_image_surface_t *)pad_image)->width; + *height = ((cairo_image_surface_t *)pad_image)->height; + *origin_x = x; + *origin_y = y; BAIL: + if (pad_image != &image->base) + cairo_surface_destroy (pad_image); + _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; @@ -1560,7 +1945,7 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, int alpha = 0; status = _cairo_surface_get_extents (meta_surface, &meta_extents); - if (status) + if (unlikely (status)) return status; old_width = surface->width; @@ -1577,13 +1962,13 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; _cairo_pdf_group_resources_clear (&surface->resources); status = _cairo_pdf_surface_open_content_stream (surface, TRUE); - if (status) + if (unlikely (status)) return status; *resource = surface->content; if (cairo_surface_get_content (meta_surface) == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -1596,11 +1981,11 @@ _cairo_pdf_surface_emit_meta_surface (cairo_pdf_surface_t *surface, status = _cairo_meta_surface_replay_region (meta_surface, &surface->base, CAIRO_META_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (status) + if (unlikely (status)) return status; status = _cairo_surface_set_clip (&surface->base, old_clip); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_close_content_stream (surface); @@ -1626,6 +2011,8 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_rectangle_int_t surface_extents; int pattern_width = 0; /* squelch bogus compiler warning */ int pattern_height = 0; /* squelch bogus compiler warning */ + int origin_x = 0; /* squelch bogus compiler warning */ + int origin_y = 0; /* squelch bogus compiler warning */ int bbox_x, bbox_y; char draw_surface[200]; @@ -1636,33 +2023,34 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_meta_surface (surface, meta_surface, &pattern_resource); - if (status) + if (unlikely (status)) return status; status = _cairo_surface_get_extents (meta_surface, &pattern_extents); - if (status) + if (unlikely (status)) return status; pattern_width = pattern_extents.width; pattern_height = pattern_extents.height; } else { status = _cairo_pdf_surface_emit_image_surface (surface, - pattern, + pdf_pattern, &pattern_resource, &pattern_width, - &pattern_height); - if (status) + &pattern_height, + &origin_x, + &origin_y); + if (unlikely (status)) return status; } status = _cairo_surface_get_extents (&surface->base, &surface_extents); - if (status) + if (unlikely (status)) return status; bbox_x = pattern_width; bbox_y = pattern_height; switch (extend) { - /* We implement EXTEND_PAD like EXTEND_NONE for now */ case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { @@ -1746,12 +2134,13 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_matrix_translate (&pdf_p2d, 0.0, surface_extents.height); cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); + cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y); cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + _cairo_pdf_surface_update_object (surface, pdf_pattern->hash_entry->pattern_res); status = _cairo_pdf_surface_open_stream (surface, - &pdf_pattern->pattern_res, + &pdf_pattern->hash_entry->pattern_res, FALSE, " /PatternType 1\n" " /BBox [0 0 %d %d]\n" @@ -1768,7 +2157,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, pdf_p2d.x0, pdf_p2d.y0, pattern_resource.id, pattern_resource.id); - if (status) + if (unlikely (status)) return status; if (_cairo_surface_is_meta (pattern->surface)) { @@ -1806,7 +2195,7 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, } status = _cairo_pdf_surface_close_stream (surface); - if (status) + if (unlikely (status)) return status; return _cairo_output_stream_get_status (surface->output); @@ -1938,14 +2327,14 @@ _cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, &stops[i], &stops[i+1], &stops[i].resource); - if (status) + if (unlikely (status)) return status; } else { status = cairo_pdf_surface_emit_rgb_linear_function (surface, &stops[i], &stops[i+1], &stops[i].resource); - if (status) + if (unlikely (status)) return status; } } @@ -2027,7 +2416,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, alpha_function->id = 0; allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t)); - if (allstops == NULL) + if (unlikely (allstops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); stops = &allstops[1]; @@ -2074,7 +2463,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, &stops[0], &stops[1], color_function); - if (status) + if (unlikely (status)) goto BAIL; if (emit_alpha) { @@ -2082,7 +2471,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, &stops[0], &stops[1], alpha_function); - if (status) + if (unlikely (status)) goto BAIL; } } else { @@ -2093,7 +2482,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, stops, FALSE, color_function); - if (status) + if (unlikely (status)) goto BAIL; if (emit_alpha) { @@ -2102,7 +2491,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, stops, TRUE, alpha_function); - if (status) + if (unlikely (status)) goto BAIL; } } @@ -2204,7 +2593,7 @@ cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, surface->height, gradient_mask.id, gradient_mask.id); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -2219,7 +2608,7 @@ cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, surface->height); status = _cairo_pdf_surface_close_stream (surface); - if (status) + if (unlikely (status)) return status; smask_resource = _cairo_pdf_surface_new_object (surface); @@ -2342,7 +2731,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, &pattern->base, &color_function, &alpha_function); - if (status) + if (unlikely (status)) return status; if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || @@ -2352,7 +2741,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, &color_function, repeat_begin, repeat_end); - if (status) + if (unlikely (status)) return status; if (alpha_function.id != 0) { @@ -2361,12 +2750,12 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, &alpha_function, repeat_begin, repeat_end); - if (status) + if (unlikely (status)) return status; } } - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + _cairo_pdf_surface_update_object (surface, pdf_pattern->hash_entry->pattern_res); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pattern\n" @@ -2378,7 +2767,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, " /Coords [ %f %f %f %f ]\n" " /Domain [ %f %f ]\n" " /Function %d 0 R\n", - pdf_pattern->pattern_res.id, + pdf_pattern->hash_entry->pattern_res.id, pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0, @@ -2402,7 +2791,7 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; - assert (pdf_pattern->gstate_res.id != 0); + assert (pdf_pattern->hash_entry->gstate_res.id != 0); /* Create pattern for SMask. */ mask_resource = _cairo_pdf_surface_new_object (surface); @@ -2441,13 +2830,13 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, ">>\n" "endobj\n"); status = _cairo_pdf_surface_add_pattern (surface, mask_resource); - if (status) + if (unlikely (status)) return status; status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern->gstate_res, + pdf_pattern->hash_entry->gstate_res, mask_resource); - if (status) + if (unlikely (status)) return status; } @@ -2473,7 +2862,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, &pattern->base, &color_function, &alpha_function); - if (status) + if (unlikely (status)) return status; pat_to_pdf = pattern->base.base.matrix; @@ -2489,7 +2878,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, y2 = _cairo_fixed_to_double (pattern->c2.y); r2 = _cairo_fixed_to_double (pattern->r2); - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + _cairo_pdf_surface_update_object (surface, pdf_pattern->hash_entry->pattern_res); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" @@ -2501,7 +2890,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, " /ColorSpace /DeviceRGB\n" " /Coords [ %f %f %f %f %f %f ]\n" " /Function %d 0 R\n", - pdf_pattern->pattern_res.id, + pdf_pattern->hash_entry->pattern_res.id, pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0, @@ -2524,7 +2913,7 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; - assert (pdf_pattern->gstate_res.id != 0); + assert (pdf_pattern->hash_entry->gstate_res.id != 0); /* Create pattern for SMask. */ mask_resource = _cairo_pdf_surface_new_object (surface); @@ -2562,9 +2951,9 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, "endobj\n"); status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern->gstate_res, + pdf_pattern->hash_entry->gstate_res, mask_resource); - if (status) + if (unlikely (status)) return status; } @@ -2616,16 +3005,16 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern static cairo_status_t _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_pdf_resource_t pattern_res, cairo_bool_t is_stroke) { cairo_status_t status; int alpha; - cairo_color_t *solid_color = NULL; + const cairo_color_t *solid_color = NULL; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern; solid_color = &solid->color; } @@ -2647,7 +3036,7 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, surface->current_color_is_stroke != is_stroke) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -2671,11 +3060,11 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, surface->current_color_alpha != solid_color->alpha) { status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -2687,15 +3076,15 @@ _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, surface->current_pattern_is_solid_color = TRUE; } else { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_pattern (surface, pattern_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; /* fill-stroke calls select_pattern twice. Don't save if the @@ -2729,7 +3118,7 @@ _cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) if (surface->select_pattern_gstate_saved) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); @@ -2747,11 +3136,11 @@ _cairo_pdf_surface_show_page (void *abstract_surface) cairo_int_status_t status; status = _cairo_pdf_surface_close_content_stream (surface); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_write_page (surface); - if (status) + if (unlikely (status)) return status; _cairo_pdf_surface_clear (surface); @@ -2790,7 +3179,7 @@ _cairo_pdf_surface_intersect_clip_path (void *abstract_surface, if (path == NULL) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q q\n"); @@ -2877,7 +3266,7 @@ _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (status) + if (unlikely (status)) return status; } @@ -2904,6 +3293,85 @@ _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } +/* Bob Jenkins hash + * + * Public domain code from: + * http://burtleburtle.net/bob/hash/doobs.html + */ + +#define HASH_MIX(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +static uint32_t +_hash_data (const unsigned char *data, int length, uint32_t initval) +{ + uint32_t a, b, c, len; + + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + while (len >= 12) { + a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24)); + b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24)); + c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24)); + HASH_MIX (a,b,c); + data += 12; + len -= 12; + } + + c += length; + switch(len) { + case 11: c+= ((uint32_t) data[10] << 24); + case 10: c+= ((uint32_t) data[9] << 16); + case 9 : c+= ((uint32_t) data[8] << 8); + case 8 : b+= ((uint32_t) data[7] << 24); + case 7 : b+= ((uint32_t) data[6] << 16); + case 6 : b+= ((uint32_t) data[5] << 8); + case 5 : b+= data[4]; + case 4 : a+= ((uint32_t) data[3] << 24); + case 3 : a+= ((uint32_t) data[2] << 16); + case 2 : a+= ((uint32_t) data[1] << 8); + case 1 : a+= data[0]; + } + HASH_MIX (a,b,c); + + return c; +} + +static void +_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, + const char *font_name, + char *tag) +{ + uint32_t hash; + int i; + long numerator; + ldiv_t d; + + hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0); + hash = _hash_data ((unsigned char *) (font_subset->glyphs), + font_subset->num_glyphs * sizeof(unsigned long), hash); + + numerator = abs (hash); + for (i = 0; i < 6; i++) { + d = ldiv (numerator, 26); + numerator = d.quot; + tag[i] = 'A' + d.rem; + } + tag[i] = 0; +} + static cairo_int_status_t _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, @@ -2919,7 +3387,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, NULL, surface->compress_content, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -2971,7 +3439,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, } status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, font_subset->utf8[i + 1]); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -3000,6 +3468,9 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_pdf_font_t font; unsigned int i; cairo_status_t status; + char tag[10]; + + _create_font_subset_tag (font_subset, subset->ps_name, tag); subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, @@ -3011,20 +3482,20 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, NULL, TRUE, " /Subtype /CIDFontType0C\n"); - if (status) + if (unlikely (status)) return status; stream = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, subset->data, subset->data_length); status = _cairo_pdf_surface_close_stream (surface); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, TRUE, &to_unicode_stream); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; descriptor = _cairo_pdf_surface_new_object (surface); @@ -3034,7 +3505,18 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /FontDescriptor\n" - " /FontName /%s\n" + " /FontName /%s+%s\n", + descriptor.id, + tag, + subset->ps_name); + + if (subset->font_name) { + _cairo_output_stream_printf (surface->output, + " /FontFamily (%s)\n", + subset->font_name); + } + + _cairo_output_stream_printf (surface->output, " /Flags 4\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" @@ -3046,8 +3528,6 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, " /FontFile3 %u 0 R\n" ">>\n" "endobj\n", - descriptor.id, - subset->base_font, subset->x_min, subset->y_min, subset->x_max, @@ -3064,7 +3544,7 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /CIDFontType0\n" - " /BaseFont /%s\n" + " /BaseFont /%s+%s\n" " /CIDSystemInfo\n" " << /Registry (Adobe)\n" " /Ordering (Identity)\n" @@ -3073,7 +3553,8 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, " /FontDescriptor %d 0 R\n" " /W [0 [", cidfont_dict.id, - subset->base_font, + tag, + subset->ps_name, descriptor.id); for (i = 0; i < font_subset->num_glyphs; i++) @@ -3091,11 +3572,12 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type0\n" - " /BaseFont /%s\n" + " /BaseFont /%s+%s\n" " /Encoding /Identity-H\n" " /DescendantFonts [ %d 0 R]\n", subset_resource.id, - subset->base_font, + tag, + subset->ps_name, cidfont_dict.id); if (to_unicode_stream.id != 0) @@ -3126,7 +3608,7 @@ _cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_cff_subset_init (&subset, name, font_subset); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); @@ -3147,7 +3629,7 @@ _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_cff_fallback_init (&subset, name, font_subset); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); @@ -3167,6 +3649,9 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, cairo_status_t status; unsigned long length; unsigned int i; + char tag[10]; + + _create_font_subset_tag (font_subset, subset->base_font, tag); subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, @@ -3184,19 +3669,19 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, " /Length3 0\n", subset->header_length, subset->data_length); - if (status) + if (unlikely (status)) return status; stream = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, subset->data, length); status = _cairo_pdf_surface_close_stream (surface); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, FALSE, &to_unicode_stream); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; descriptor = _cairo_pdf_surface_new_object (surface); @@ -3206,7 +3691,7 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /FontDescriptor\n" - " /FontName /%s\n" + " /FontName /%s+%s\n" " /Flags 4\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" @@ -3219,6 +3704,7 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, ">>\n" "endobj\n", descriptor.id, + tag, subset->base_font, subset->x_min, subset->y_min, @@ -3233,12 +3719,13 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type1\n" - " /BaseFont /%s\n" + " /BaseFont /%s+%s\n" " /FirstChar 0\n" " /LastChar %d\n" " /FontDescriptor %d 0 R\n" " /Widths [", subset_resource.id, + tag, subset->base_font, font_subset->num_glyphs - 1, descriptor.id); @@ -3278,7 +3765,7 @@ _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); @@ -3299,7 +3786,7 @@ _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); @@ -3320,6 +3807,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, cairo_pdf_font_t font; cairo_truetype_subset_t subset; unsigned int i; + char tag[10]; subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, @@ -3328,15 +3816,17 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; status = _cairo_truetype_subset_init (&subset, font_subset); - if (status) + if (unlikely (status)) return status; + _create_font_subset_tag (font_subset, subset.ps_name, tag); + status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, " /Length1 %lu\n", subset.data_length); - if (status) { + if (unlikely (status)) { _cairo_truetype_subset_fini (&subset); return status; } @@ -3345,7 +3835,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, _cairo_output_stream_write (surface->output, subset.data, subset.data_length); status = _cairo_pdf_surface_close_stream (surface); - if (status) { + if (unlikely (status)) { _cairo_truetype_subset_fini (&subset); return status; } @@ -3353,7 +3843,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, TRUE, &to_unicode_stream); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) { + if (_cairo_status_is_error (status)) { _cairo_truetype_subset_fini (&subset); return status; } @@ -3367,7 +3857,18 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /FontDescriptor\n" - " /FontName /%s\n" + " /FontName /%s+%s\n", + descriptor.id, + tag, + subset.ps_name); + + if (subset.font_name) { + _cairo_output_stream_printf (surface->output, + " /FontFamily (%s)\n", + subset.font_name); + } + + _cairo_output_stream_printf (surface->output, " /Flags 4\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" @@ -3379,8 +3880,6 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, " /FontFile2 %u 0 R\n" ">>\n" "endobj\n", - descriptor.id, - subset.base_font, (long)(subset.x_min*PDF_UNITS_PER_EM), (long)(subset.y_min*PDF_UNITS_PER_EM), (long)(subset.x_max*PDF_UNITS_PER_EM), @@ -3400,7 +3899,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /CIDFontType2\n" - " /BaseFont /%s\n" + " /BaseFont /%s+%s\n" " /CIDSystemInfo\n" " << /Registry (Adobe)\n" " /Ordering (Identity)\n" @@ -3409,7 +3908,8 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, " /FontDescriptor %d 0 R\n" " /W [0 [", cidfont_dict.id, - subset.base_font, + tag, + subset.ps_name, descriptor.id); for (i = 0; i < font_subset->num_glyphs; i++) @@ -3427,11 +3927,12 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type0\n" - " /BaseFont /%s\n" + " /BaseFont /%s+%s\n" " /Encoding /Identity-H\n" " /DescendantFonts [ %d 0 R]\n", subset_resource.id, - subset.base_font, + tag, + subset.ps_name, cidfont_dict.id); if (to_unicode_stream.id != 0) @@ -3509,6 +4010,11 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su null_stream, _cairo_pdf_emit_imagemask, surface->font_subsets); + if (unlikely (type3_surface->status)) { + status2 = _cairo_output_stream_destroy (null_stream); + return type3_surface->status; + } + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, _cairo_pdf_surface_add_font, surface); @@ -3516,7 +4022,7 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, font_subset->glyphs[i]); - if (status) + if (unlikely (status)) break; } @@ -3551,11 +4057,11 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t)); - if (glyphs == NULL) + if (unlikely (glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double)); - if (widths == NULL) { + if (unlikely (widths == NULL)) { free (glyphs); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -3565,6 +4071,12 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, NULL, _cairo_pdf_emit_imagemask, surface->font_subsets); + if (unlikely (type3_surface->status)) { + free (glyphs); + free (widths); + return type3_surface->status; + } + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, _cairo_pdf_surface_add_font, surface); @@ -3574,7 +4086,7 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, NULL, surface->compress_content, NULL); - if (status) + if (unlikely (status)) break; glyphs[i] = surface->pdf_stream.self; @@ -3583,11 +4095,11 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, font_subset->glyphs[i], &bbox, &widths[i]); - if (status) + if (unlikely (status)) break; status = _cairo_pdf_surface_close_stream (surface); - if (status) + if (unlikely (status)) break; if (i == 0) { @@ -3607,7 +4119,7 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, } } cairo_surface_destroy (type3_surface); - if (status) { + if (unlikely (status)) { free (glyphs); free (widths); return status; @@ -3655,7 +4167,7 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, FALSE, &to_unicode_stream); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) { + if (_cairo_status_is_error (status)) { free (widths); return status; } @@ -3766,19 +4278,19 @@ _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_pdf_surface_analyze_user_font_subset, surface); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, _cairo_pdf_surface_emit_unscaled_font_subset, surface); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, _cairo_pdf_surface_emit_scaled_font_subset, surface); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, @@ -3853,35 +4365,36 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, /* Create mask group */ status = _cairo_pdf_surface_open_group (surface, NULL); - if (status) + if (unlikely (status)) return status; pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, &pattern_res, &gstate_res); - if (status) + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL, + &pattern_res, &gstate_res); + if (unlikely (status)) return status; if (gstate_res.id != 0) { smask_group = _cairo_pdf_surface_create_smask_group (surface); - if (smask_group == NULL) + if (unlikely (smask_group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); smask_group->operation = PDF_PAINT; smask_group->source = cairo_pattern_reference (group->mask); smask_group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (smask_group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -3890,7 +4403,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, smask_group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -3898,45 +4411,46 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, surface->width, surface->height); status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; } status = _cairo_pdf_surface_close_group (surface, &mask_group); - if (status) + if (unlikely (status)) return status; /* Create source group */ status = _cairo_pdf_surface_open_group (surface, &group->source_res); - if (status) + if (unlikely (status)) return status; pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, &pattern_res, &gstate_res); - if (status) + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL, + &pattern_res, &gstate_res); + if (unlikely (status)) return status; if (gstate_res.id != 0) { smask_group = _cairo_pdf_surface_create_smask_group (surface); - if (smask_group == NULL) + if (unlikely (smask_group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); smask_group->operation = PDF_PAINT; smask_group->source = cairo_pattern_reference (group->source); smask_group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (smask_group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -3945,7 +4459,7 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, smask_group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -3953,12 +4467,12 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, surface->width, surface->height); status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; } status = _cairo_pdf_surface_close_group (surface, NULL); - if (status) + if (unlikely (status)) return status; /* Create an smask based on the alpha component of mask_group */ @@ -4011,14 +4525,14 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, return _cairo_pdf_surface_write_mask_group (surface, group); status = _cairo_pdf_surface_open_group (surface, &group->group_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_select_pattern (surface, group->source, group->source_res, group->operation == PDF_STROKE); - if (status) + if (unlikely (status)) return status; switch (group->operation) { @@ -4051,11 +4565,11 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, group->scaled_font); break; } - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_close_group (surface, NULL); @@ -4084,20 +4598,20 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface */ pattern_index = 0; group_index = 0; - while ((pattern_index < _cairo_array_num_elements (&surface->patterns)) || + while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || (group_index < _cairo_array_num_elements (&surface->smask_groups))) { for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { _cairo_array_copy_element (&surface->smask_groups, group_index, &group); status = _cairo_pdf_surface_write_smask_group (surface, group); - if (status) + if (unlikely (status)) return status; } - for (; pattern_index < _cairo_array_num_elements (&surface->patterns); pattern_index++) { - _cairo_array_copy_element (&surface->patterns, pattern_index, &pattern); + for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) { + _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern); status = _cairo_pdf_surface_emit_pattern (surface, &pattern); - if (status) + if (unlikely (status)) return status; } } @@ -4115,7 +4629,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) _cairo_pdf_group_resources_clear (&surface->resources); if (surface->has_fallback_images) { status = _cairo_pdf_surface_open_knockout_group (surface); - if (status) + if (unlikely (status)) return status; len = _cairo_array_num_elements (&surface->knockout_group); @@ -4125,34 +4639,34 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) "/x%d Do\n", res.id); status = _cairo_pdf_surface_add_xobject (surface, res); - if (status) + if (unlikely (status)) return status; } _cairo_output_stream_printf (surface->output, "/x%d Do\n", surface->content.id); status = _cairo_pdf_surface_add_xobject (surface, surface->content); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_close_group (surface, &knockout); - if (status) + if (unlikely (status)) return status; _cairo_pdf_group_resources_clear (&surface->resources); status = _cairo_pdf_surface_open_content_stream (surface, FALSE); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/x%d Do\n", knockout.id); status = _cairo_pdf_surface_add_xobject (surface, knockout); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_close_content_stream (surface); - if (status) + if (unlikely (status)) return status; } @@ -4182,11 +4696,11 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) surface->content_resources.id); status = _cairo_array_append (&surface->pages, &page); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -4204,7 +4718,7 @@ _cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (status) + if (unlikely (status)) return status; if (image->base.status) @@ -4257,11 +4771,11 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern) } static cairo_bool_t -_gradient_pattern_supported (cairo_pattern_t *pattern) +_gradient_pattern_supported (const cairo_pattern_t *pattern) { cairo_extend_t extend; - extend = cairo_pattern_get_extend (pattern); + extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); /* Radial gradients are currently only supported with EXTEND_NONE @@ -4292,7 +4806,7 @@ _gradient_pattern_supported (cairo_pattern_t *pattern) } static cairo_bool_t -_pattern_supported (cairo_pattern_t *pattern) +_pattern_supported (const cairo_pattern_t *pattern) { if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) return TRUE; @@ -4310,10 +4824,13 @@ _pattern_supported (cairo_pattern_t *pattern) static cairo_int_status_t _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { - if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { return CAIRO_INT_STATUS_UNSUPPORTED; + } if (! _pattern_supported (pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -4322,8 +4839,12 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_meta (surface_pattern->surface)) - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + if ( _cairo_surface_is_meta (surface_pattern->surface)) { + if (pattern->extend == CAIRO_EXTEND_PAD) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + } } } @@ -4368,7 +4889,7 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, static cairo_bool_t _cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { if (_cairo_pdf_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) return TRUE; @@ -4382,11 +4903,11 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) cairo_status_t status; status = _cairo_pdf_surface_close_content_stream (surface); - if (status) + if (unlikely (status)) return status; status = _cairo_array_append (&surface->knockout_group, &surface->content); - if (status) + if (unlikely (status)) return status; _cairo_pdf_group_resources_clear (&surface->resources); @@ -4396,7 +4917,8 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) static cairo_int_status_t _cairo_pdf_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; @@ -4407,7 +4929,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, return _cairo_pdf_surface_analyze_operation (surface, op, source); } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); - if (status) + if (unlikely (status)) return status; } @@ -4415,36 +4937,41 @@ _cairo_pdf_surface_paint (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &pattern_res, &gstate_res); + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + &pattern_res, &gstate_res); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); - if (group == NULL) + if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); group->operation = PDF_PAINT; - group->source = cairo_pattern_reference (source); + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -4453,7 +4980,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -4461,7 +4988,7 @@ _cairo_pdf_surface_paint (void *abstract_surface, surface->width, surface->height); status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; } @@ -4471,8 +4998,9 @@ _cairo_pdf_surface_paint (void *abstract_surface, static cairo_int_status_t _cairo_pdf_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; @@ -4493,7 +5021,7 @@ _cairo_pdf_surface_mask (void *abstract_surface, mask_status); } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); - if (status) + if (unlikely (status)) return status; } @@ -4501,12 +5029,20 @@ _cairo_pdf_surface_mask (void *abstract_surface, assert (_cairo_pdf_surface_operation_supported (surface, op, mask)); group = _cairo_pdf_surface_create_smask_group (surface); - if (group == NULL) + if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); group->operation = PDF_MASK; - group->source = cairo_pattern_reference (source); - group->mask = cairo_pattern_reference (mask); + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } + status = _cairo_pattern_create_copy (&group->mask, mask); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } group->source_res = _cairo_pdf_surface_new_object (surface); if (group->source_res.id == 0) { _cairo_pdf_smask_group_destroy (group); @@ -4514,21 +5050,21 @@ _cairo_pdf_surface_mask (void *abstract_surface, } status = _cairo_pdf_surface_add_smask_group (surface, group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } status = _cairo_pdf_surface_add_smask (surface, group->group_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, group->source_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -4542,13 +5078,14 @@ _cairo_pdf_surface_mask (void *abstract_surface, static cairo_int_status_t _cairo_pdf_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; @@ -4562,22 +5099,27 @@ _cairo_pdf_surface_stroke (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &pattern_res, &gstate_res); + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + &pattern_res, &gstate_res); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); - if (group == NULL) + if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); group->operation = PDF_STROKE; - group->source = cairo_pattern_reference (source); + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } group->source_res = pattern_res; status = _cairo_path_fixed_init_copy (&group->path, path); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } @@ -4586,21 +5128,21 @@ _cairo_pdf_surface_stroke (void *abstract_surface, group->ctm = *ctm; group->ctm_inverse = *ctm_inverse; status = _cairo_pdf_surface_add_smask_group (surface, group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -4609,7 +5151,7 @@ _cairo_pdf_surface_stroke (void *abstract_surface, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_stroke (&surface->pdf_operators, @@ -4617,11 +5159,11 @@ _cairo_pdf_surface_stroke (void *abstract_surface, style, ctm, ctm_inverse); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; } @@ -4631,11 +5173,12 @@ _cairo_pdf_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_pdf_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; @@ -4646,7 +5189,7 @@ _cairo_pdf_surface_fill (void *abstract_surface, return _cairo_pdf_surface_analyze_operation (surface, op, source); } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); - if (status) + if (unlikely (status)) return status; } @@ -4654,43 +5197,48 @@ _cairo_pdf_surface_fill (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &pattern_res, &gstate_res); + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + &pattern_res, &gstate_res); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); - if (group == NULL) + if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); group->operation = PDF_FILL; - group->source = cairo_pattern_reference (source); + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } group->source_res = pattern_res; status = _cairo_path_fixed_init_copy (&group->path, path); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } group->fill_rule = fill_rule; status = _cairo_pdf_surface_add_smask_group (surface, group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -4699,17 +5247,17 @@ _cairo_pdf_surface_fill (void *abstract_surface, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; } @@ -4719,18 +5267,19 @@ _cairo_pdf_surface_fill (void *abstract_surface, static cairo_int_status_t _cairo_pdf_surface_fill_stroke (void *abstract_surface, cairo_operator_t fill_op, - cairo_pattern_t *fill_source, + const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, - cairo_pattern_t *stroke_source, + const cairo_pattern_t *stroke_source, cairo_stroke_style_t *stroke_style, cairo_matrix_t *stroke_ctm, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, - cairo_antialias_t stroke_antialias) + cairo_antialias_t stroke_antialias, + cairo_rectangle_int_t *extents) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; @@ -4758,9 +5307,10 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, + extents, &fill_pattern_res, &gstate_res); - if (status) + if (unlikely (status)) return status; assert (gstate_res.id == 0); @@ -4769,9 +5319,10 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, + extents, &stroke_pattern_res, &gstate_res); - if (status) + if (unlikely (status)) return status; assert (gstate_res.id == 0); @@ -4780,12 +5331,12 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, * select both at the same time */ status = _cairo_pdf_surface_select_pattern (surface, fill_source, fill_pattern_res, FALSE); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_select_pattern (surface, stroke_source, stroke_pattern_res, TRUE); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, @@ -4794,11 +5345,11 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, stroke_style, stroke_ctm, stroke_ctm_inverse); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; return _cairo_output_stream_get_status (surface->output); @@ -4812,8 +5363,8 @@ _cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface) static cairo_int_status_t _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, + cairo_operator_t op, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -4821,7 +5372,8 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) { cairo_pdf_surface_t *surface = abstract_surface; cairo_status_t status; @@ -4835,24 +5387,29 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &pattern_res, &gstate_res); + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, extents, + &pattern_res, &gstate_res); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface); - if (group == NULL) + if (unlikely (group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); group->operation = PDF_SHOW_GLYPHS; - group->source = cairo_pattern_reference (source); + status = _cairo_pattern_create_copy (&group->source, source); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + return status; + } group->source_res = pattern_res; if (utf8_len) { group->utf8 = malloc (utf8_len); - if (group->utf8 == NULL) { + if (unlikely (group->utf8 == NULL)) { _cairo_pdf_smask_group_destroy (group); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -4862,7 +5419,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, if (num_glyphs) { group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (group->glyphs == NULL) { + if (unlikely (group->glyphs == NULL)) { _cairo_pdf_smask_group_destroy (group); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -4872,7 +5429,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, if (num_clusters) { group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); - if (group->clusters == NULL) { + if (unlikely (group->clusters == NULL)) { _cairo_pdf_smask_group_destroy (group); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -4882,21 +5439,21 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, group->scaled_font = cairo_scaled_font_reference (scaled_font); status = _cairo_pdf_surface_add_smask_group (surface, group); - if (status) { + if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, @@ -4905,7 +5462,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (status) + if (unlikely (status)) return status; /* Each call to show_glyphs() with a transclucent pattern must @@ -4914,7 +5471,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, * each other. */ if (! _cairo_pattern_is_opaque (source)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; } @@ -4924,11 +5481,11 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, clusters, num_clusters, cluster_flags, scaled_font); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_surface_unselect_pattern (surface); - if (status) + if (unlikely (status)) return status; } @@ -4957,6 +5514,8 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* _cairo_pdf_surface_copy_page */ _cairo_pdf_surface_show_page, NULL, /* set_clip_region */ @@ -4982,6 +5541,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { NULL, /* reset */ _cairo_pdf_surface_fill_stroke, NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ _cairo_pdf_surface_has_show_text_glyphs, _cairo_pdf_surface_show_text_glyphs, }; diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 1a066b3..c0de538 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -43,6 +43,21 @@ CAIRO_BEGIN_DECLS +/** + * cairo_pdf_version_t: + * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. + * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. + * + * #cairo_pdf_version_t is used to describe the version number of the PDF + * specification that a generated PDF file will conform to. + * + * Since 1.10 + */ +typedef enum _cairo_pdf_version { + CAIRO_PDF_VERSION_1_4, + CAIRO_PDF_VERSION_1_5 +} cairo_pdf_version_t; + cairo_public cairo_surface_t * cairo_pdf_surface_create (const char *filename, double width_in_points, @@ -55,6 +70,17 @@ cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, double height_in_points); cairo_public void +cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface, + cairo_pdf_version_t version); + +cairo_public void +cairo_pdf_get_versions (cairo_pdf_version_t const **versions, + int *num_versions); + +cairo_public const char * +cairo_pdf_version_to_string (cairo_pdf_version_t version); + +cairo_public void cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points); diff --git a/src/cairo-pen.c b/src/cairo-pen.c index 425b3b9..b2fd855 100644 --- a/src/cairo-pen.c +++ b/src/cairo-pen.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -32,28 +33,33 @@ * * Contributor(s): * Carl D. Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" static int -_cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix); +_cairo_pen_vertices_needed (double tolerance, + double radius, + const cairo_matrix_t *matrix); static void _cairo_pen_compute_slopes (cairo_pen_t *pen); -static void -_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon); - cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, double tolerance, - cairo_matrix_t *ctm) + const cairo_matrix_t *ctm) { int i; int reflect; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); + pen->radius = radius; pen->tolerance = tolerance; @@ -66,7 +72,7 @@ _cairo_pen_init (cairo_pen_t *pen, if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { pen->vertices = _cairo_malloc_ab (pen->num_vertices, sizeof (cairo_pen_vertex_t)); - if (pen->vertices == NULL) + if (unlikely (pen->vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { pen->vertices = pen->vertices_embedded; @@ -99,21 +105,26 @@ _cairo_pen_fini (cairo_pen_t *pen) if (pen->vertices != pen->vertices_embedded) free (pen->vertices); - pen->vertices = pen->vertices_embedded; - pen->num_vertices = 0; + + VG (VALGRIND_MAKE_MEM_NOACCESS (pen, sizeof (cairo_pen_t))); } cairo_status_t -_cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other) +_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); + *pen = *other; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + pen->vertices = pen->vertices_embedded; if (pen->num_vertices) { if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { pen->vertices = _cairo_malloc_ab (pen->num_vertices, sizeof (cairo_pen_vertex_t)); - if (pen->vertices == NULL) + if (unlikely (pen->vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -131,6 +142,9 @@ _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) int num_vertices; int i; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + num_vertices = pen->num_vertices + num_points; if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || pen->vertices != pen->vertices_embedded) @@ -140,7 +154,7 @@ _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) if (pen->vertices == pen->vertices_embedded) { vertices = _cairo_malloc_ab (num_vertices, sizeof (cairo_pen_vertex_t)); - if (vertices == NULL) + if (unlikely (vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (vertices, pen->vertices, @@ -149,7 +163,7 @@ _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) vertices = _cairo_realloc_ab (pen->vertices, num_vertices, sizeof (cairo_pen_vertex_t)); - if (vertices == NULL) + if (unlikely (vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -163,7 +177,7 @@ _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) pen->vertices[pen->num_vertices-num_points+i].point = point[i]; status = _cairo_hull_compute (pen->vertices, &pen->num_vertices); - if (status) + if (unlikely (status)) return status; _cairo_pen_compute_slopes (pen); @@ -259,7 +273,7 @@ doesn't matter where on the circle the error is computed. static int _cairo_pen_vertices_needed (double tolerance, double radius, - cairo_matrix_t *matrix) + const cairo_matrix_t *matrix) { /* * the pen is a circle that gets transformed to an ellipse by matrix. @@ -267,7 +281,8 @@ _cairo_pen_vertices_needed (double tolerance, * we don't need the minor axis length. */ - double major_axis = _cairo_matrix_transformed_circle_major_axis(matrix, radius); + double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, + radius); /* * compute number of vertices needed @@ -323,10 +338,9 @@ _cairo_pen_compute_slopes (cairo_pen_t *pen) * pen's "extra points" from the spline's initial and final slopes are * properly found when beginning the spline stroking.] */ -void -_cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen, - cairo_slope_t *slope, - int *active) +int +_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope) { int i; @@ -344,7 +358,7 @@ _cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen, if (i == pen->num_vertices) i = 0; - *active = i; + return i; } /* Find active pen vertex for counterclockwise edge of stroke at the given slope. @@ -352,13 +366,12 @@ _cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen, * Note: See the comments for _cairo_pen_find_active_cw_vertex_index * for some details about the strictness of the inequalities here. */ -void -_cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen, - cairo_slope_t *slope, - int *active) +int +_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope) { - int i; cairo_slope_t slope_reverse; + int i; slope_reverse = *slope; slope_reverse.dx = -slope_reverse.dx; @@ -378,56 +391,26 @@ _cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen, if (i < 0) i = pen->num_vertices - 1; - *active = i; + return i; } -static void -_cairo_pen_stroke_spline_half (cairo_pen_t *pen, - cairo_spline_t *spline, - cairo_direction_t dir, - cairo_polygon_t *polygon) +static int +_cairo_pen_stroke_spline_add_convolved_point (cairo_pen_stroke_spline_t *stroker, + const cairo_point_t *last_point, + const cairo_slope_t *slope, + cairo_point_t *last_hull_point, + int active, + int step) { - int i; - int start, stop, step; - int active = 0; - cairo_point_t hull_point; - cairo_slope_t slope, initial_slope, final_slope; - cairo_point_t *point = spline->points; - int num_points = spline->num_points; - - if (dir == CAIRO_DIRECTION_FORWARD) { - start = 0; - stop = num_points; - step = 1; - initial_slope = spline->initial_slope; - final_slope = spline->final_slope; - } else { - start = num_points - 1; - stop = -1; - step = -1; - initial_slope = spline->final_slope; - initial_slope.dx = -initial_slope.dx; - initial_slope.dy = -initial_slope.dy; - final_slope = spline->initial_slope; - final_slope.dx = -final_slope.dx; - final_slope.dy = -final_slope.dy; - } - - _cairo_pen_find_active_cw_vertex_index (pen, - &initial_slope, - &active); - - i = start; - while (i != stop) { - hull_point.x = point[i].x + pen->vertices[active].point.x; - hull_point.y = point[i].y + pen->vertices[active].point.y; + do { + cairo_point_t hull_point; - _cairo_polygon_line_to (polygon, &hull_point); - - if (i + step == stop) - slope = final_slope; - else - _cairo_slope_init (&slope, &point[i], &point[i+step]); + hull_point.x = last_point->x + stroker->pen.vertices[active].point.x; + hull_point.y = last_point->y + stroker->pen.vertices[active].point.y; + _cairo_polygon_add_edge (&stroker->polygon, + last_hull_point, &hull_point, + step); + *last_hull_point = hull_point; /* The strict inequalities here ensure that if a spline slope * compares identically with either of the slopes of the @@ -439,53 +422,165 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen, * consider it unequal and reject. This is due to the inherent * ambiguity when comparing slopes that differ by exactly * pi. */ - if (_cairo_slope_compare (&slope, &pen->vertices[active].slope_ccw) > 0) { - if (++active == pen->num_vertices) + if (_cairo_slope_compare (slope, + &stroker->pen.vertices[active].slope_ccw) > 0) + { + if (++active == stroker->pen.num_vertices) active = 0; - } else if (_cairo_slope_compare (&slope, &pen->vertices[active].slope_cw) < 0) { + } + else if (_cairo_slope_compare (slope, + &stroker->pen.vertices[active].slope_cw) < 0) + { if (--active == -1) - active = pen->num_vertices - 1; - } else { - i += step; + active = stroker->pen.num_vertices - 1; } - } + else + { + return active; + } + } while (TRUE); } + /* Compute outline of a given spline using the pen. - The trapezoids needed to fill that outline will be added to traps -*/ + * The trapezoids needed to fill that outline will be added to traps + */ cairo_status_t -_cairo_pen_stroke_spline (cairo_pen_t *pen, - cairo_spline_t *spline, - double tolerance, - cairo_traps_t *traps) +_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t *stroker, + double tolerance, + cairo_traps_t *traps) { cairo_status_t status; - cairo_polygon_t polygon; + cairo_slope_t slope; /* If the line width is so small that the pen is reduced to a single point, then we have nothing to do. */ - if (pen->num_vertices <= 1) + if (stroker->pen.num_vertices <= 1) return CAIRO_STATUS_SUCCESS; - _cairo_polygon_init (&polygon); + /* open the polygon */ + slope = stroker->spline.initial_slope; + stroker->forward_vertex = + _cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope); + stroker->forward_hull_point.x = stroker->last_point.x + + stroker->pen.vertices[stroker->forward_vertex].point.x; + stroker->forward_hull_point.y = stroker->last_point.y + + stroker->pen.vertices[stroker->forward_vertex].point.y; + + slope.dx = -slope.dx; + slope.dy = -slope.dy; + stroker->backward_vertex = + _cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope); + stroker->backward_hull_point.x = stroker->last_point.x + + stroker->pen.vertices[stroker->backward_vertex].point.x; + stroker->backward_hull_point.y = stroker->last_point.y + + stroker->pen.vertices[stroker->backward_vertex].point.y; + + _cairo_polygon_add_edge (&stroker->polygon, + &stroker->backward_hull_point, + &stroker->forward_hull_point, + 1); + + status = _cairo_spline_decompose (&stroker->spline, tolerance); + if (unlikely (status)) + return status; - status = _cairo_spline_decompose (spline, tolerance); - if (status) - goto BAIL; + /* close the polygon */ + slope = stroker->spline.final_slope; + _cairo_pen_stroke_spline_add_convolved_point (stroker, + &stroker->last_point, + &slope, + &stroker->forward_hull_point, + stroker->forward_vertex, + 1); + + slope.dx = -slope.dx; + slope.dy = -slope.dy; + _cairo_pen_stroke_spline_add_convolved_point (stroker, + &stroker->last_point, + &slope, + &stroker->backward_hull_point, + stroker->backward_vertex, + -1); + + _cairo_polygon_add_edge (&stroker->polygon, + &stroker->forward_hull_point, + &stroker->backward_hull_point, + 1); + + status = _cairo_polygon_status (&stroker->polygon); + if (unlikely (status)) + return status; - _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_FORWARD, &polygon); + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &stroker->polygon, + CAIRO_FILL_RULE_WINDING); - _cairo_pen_stroke_spline_half (pen, spline, CAIRO_DIRECTION_REVERSE, &polygon); + return status; +} - _cairo_polygon_close (&polygon); - status = _cairo_polygon_status (&polygon); - if (status) - goto BAIL; +static cairo_status_t +_cairo_pen_stroke_spline_add_point (void *closure, + const cairo_point_t *point) +{ + cairo_pen_stroke_spline_t *stroker = closure; + cairo_slope_t slope; + + _cairo_slope_init (&slope, &stroker->last_point, point); + stroker->forward_vertex = + _cairo_pen_stroke_spline_add_convolved_point (stroker, + &stroker->last_point, + &slope, + &stroker->forward_hull_point, + stroker->forward_vertex, + 1); + + slope.dx = -slope.dx; + slope.dy = -slope.dy; + stroker->backward_vertex = + _cairo_pen_stroke_spline_add_convolved_point (stroker, + &stroker->last_point, + &slope, + &stroker->backward_hull_point, + stroker->backward_vertex, + -1); + stroker->last_point = *point; - status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, CAIRO_FILL_RULE_WINDING); -BAIL: - _cairo_polygon_fini (&polygon); + return CAIRO_STATUS_SUCCESS; +} - return status; +cairo_int_status_t +_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker, + const cairo_pen_t *pen, + const cairo_point_t *a, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_int_status_t status; + + if (! _cairo_spline_init (&stroker->spline, + _cairo_pen_stroke_spline_add_point, + stroker, + a, b, c, d)) + { + return CAIRO_INT_STATUS_DEGENERATE; + } + + status = _cairo_pen_init_copy (&stroker->pen, pen); + if (unlikely (status)) + return status; + + _cairo_polygon_init (&stroker->polygon); + + stroker->last_point = *a; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker) +{ + _cairo_polygon_fini (&stroker->polygon); + _cairo_pen_fini (&stroker->pen); } diff --git a/src/cairo-png.c b/src/cairo-png.c index 93317f3..d4f0476 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -37,10 +37,19 @@ */ #include "cairoint.h" +#include "cairo-output-stream-private.h" +#include <stdio.h> #include <errno.h> #include <png.h> +struct png_read_closure_t { + cairo_read_func_t read_func; + void *closure; + cairo_output_stream_t *png_data; +}; + + /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ static void unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) @@ -134,6 +143,7 @@ write_png (cairo_surface_t *surface, int i; cairo_status_t status; cairo_image_surface_t *image; + cairo_image_surface_t * volatile clone; void *image_extra; png_struct *png; png_info *info; @@ -148,7 +158,7 @@ write_png (cairo_surface_t *surface, if (status == CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - else if (status) + else if (unlikely (status)) return status; /* PNG complains about "Image width or height is zero in IHDR" */ @@ -157,40 +167,55 @@ write_png (cairo_surface_t *surface, goto BAIL1; } - rows = _cairo_malloc_ab (image->height, sizeof (png_byte*)); - if (rows == NULL) { + /* Handle the various fallback formats (e.g. low bit-depth XServers) + * by coercing them to a simpler format using pixman. + */ + if (image->format == CAIRO_FORMAT_INVALID) { + clone = _cairo_image_surface_coerce (image, + _cairo_format_from_content (image->base.content)); + status = clone->base.status; + if (unlikely (status)) + goto BAIL1; + } else + clone = image; + + rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*)); + if (unlikely (rows == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL1; + goto BAIL2; } - for (i = 0; i < image->height; i++) - rows[i] = (png_byte *) image->data + i * image->stride; + for (i = 0; i < clone->height; i++) + rows[i] = (png_byte *) clone->data + i * clone->stride; png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status, png_simple_error_callback, png_simple_warning_callback); - if (png == NULL) { + if (unlikely (png == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL2; + goto BAIL3; } info = png_create_info_struct (png); - if (info == NULL) { + if (unlikely (info == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL3; + goto BAIL4; } #ifdef PNG_SETJMP_SUPPORTED if (setjmp (png_jmpbuf (png))) - goto BAIL3; + goto BAIL4; #endif png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn); - switch (image->format) { + switch (clone->format) { case CAIRO_FORMAT_ARGB32: depth = 8; - png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE) + png_color_type = PNG_COLOR_TYPE_RGB; + else + png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; case CAIRO_FORMAT_RGB24: depth = 8; @@ -209,12 +234,12 @@ write_png (cairo_surface_t *surface, break; default: status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - goto BAIL3; + goto BAIL4; } png_set_IHDR (png, info, - image->width, - image->height, depth, + clone->width, + clone->height, depth, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, @@ -237,20 +262,23 @@ write_png (cairo_surface_t *surface, */ png_write_info (png, info); - if (image->format == CAIRO_FORMAT_ARGB32) + if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { png_set_write_user_transform_fn (png, unpremultiply_data); - else if (image->format == CAIRO_FORMAT_RGB24) + } else if (png_color_type == PNG_COLOR_TYPE_RGB) { png_set_write_user_transform_fn (png, convert_data_to_bytes); - if (image->format == CAIRO_FORMAT_RGB24) png_set_filler (png, 0, PNG_FILLER_AFTER); + } png_write_image (png, rows); png_write_end (png, info); -BAIL3: +BAIL4: png_destroy_write_struct (&png, &info); -BAIL2: +BAIL3: free (rows); +BAIL2: + if (clone != image) + cairo_surface_destroy (&clone->base); BAIL1: _cairo_surface_release_source_image (surface, image, image_extra); @@ -335,7 +363,7 @@ stream_write_func (png_structp png, png_bytep data, png_size_t size) png_closure = png_get_io_ptr (png); status = png_closure->write_func (png_closure->closure, data, size); - if (status) { + if (unlikely (status)) { cairo_status_t *error = png_get_error_ptr (png); if (*error == CAIRO_STATUS_SUCCESS) *error = status; @@ -433,9 +461,45 @@ convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) } } +static cairo_status_t +stdio_read_func (void *closure, unsigned char *data, unsigned int size) +{ + FILE *file = closure; + + while (size) { + size_t ret; + + ret = fread (data, 1, size, file); + size -= ret; + data += ret; + + if (size && (feof (file) || ferror (file))) + return _cairo_error (CAIRO_STATUS_READ_ERROR); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +stream_read_func (png_structp png, png_bytep data, png_size_t size) +{ + cairo_status_t status; + struct png_read_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + status = png_closure->read_func (png_closure->closure, data, size); + if (unlikely (status)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = status; + png_error (png, NULL); + } + + _cairo_output_stream_write (png_closure->png_data, data, size); +} + static cairo_surface_t * -read_png (png_rw_ptr read_func, - void *closure) +read_png (struct png_read_closure_t *png_closure) { cairo_surface_t *surface; png_struct *png = NULL; @@ -447,24 +511,28 @@ read_png (png_rw_ptr read_func, unsigned int i; cairo_format_t format; cairo_status_t status; + unsigned char *mime_data; + unsigned int mime_data_length; + + png_closure->png_data = _cairo_memory_stream_create (); /* XXX: Perhaps we'll want some other error handlers? */ png = png_create_read_struct (PNG_LIBPNG_VER_STRING, &status, png_simple_error_callback, png_simple_warning_callback); - if (png == NULL) { + if (unlikely (png == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } info = png_create_info_struct (png); - if (info == NULL) { + if (unlikely (info == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } - png_set_read_fn (png, closure, read_func); + png_set_read_fn (png, png_closure, stream_read_func); status = CAIRO_STATUS_SUCCESS; #ifdef PNG_SETJMP_SUPPORTED @@ -479,7 +547,7 @@ read_png (png_rw_ptr read_func, png_get_IHDR (png, info, &png_width, &png_height, &depth, &color_type, &interlace, NULL, NULL); - if (status) { /* catch any early warnings */ + if (unlikely (status)) { /* catch any early warnings */ surface = _cairo_surface_create_in_error (status); goto BAIL; } @@ -555,13 +623,13 @@ read_png (png_rw_ptr read_func, } data = _cairo_malloc_ab (png_height, stride); - if (data == NULL) { + if (unlikely (data == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } row_pointers = _cairo_malloc_ab (png_height, sizeof (char *)); - if (row_pointers == NULL) { + if (unlikely (row_pointers == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } @@ -572,7 +640,7 @@ read_png (png_rw_ptr read_func, png_read_image (png, row_pointers); png_read_end (png, info); - if (status) { /* catch any late warnings - probably hit an error already */ + if (unlikely (status)) { /* catch any late warnings - probably hit an error already */ surface = _cairo_surface_create_in_error (status); goto BAIL; } @@ -586,34 +654,45 @@ read_png (png_rw_ptr read_func, _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface); data = NULL; + _cairo_debug_check_image_surface_is_defined (surface); + + status = _cairo_memory_stream_destroy (png_closure->png_data, + &mime_data, + &mime_data_length); + png_closure->png_data = NULL; + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + status = cairo_surface_set_mime_data (surface, + CAIRO_MIME_TYPE_PNG, + mime_data, + mime_data_length, + free, + mime_data); + if (unlikely (status)) { + free (mime_data); + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + BAIL: - if (row_pointers) + if (row_pointers != NULL) free (row_pointers); - if (data) + if (data != NULL) free (data); - if (png) + if (png != NULL) png_destroy_read_struct (&png, &info, NULL); + if (png_closure->png_data != NULL) { + cairo_status_t status_ignored; - return surface; -} - -static void -stdio_read_func (png_structp png, png_bytep data, png_size_t size) -{ - FILE *fp; - - fp = png_get_io_ptr (png); - while (size) { - size_t ret = fread (data, 1, size, fp); - size -= ret; - data += ret; - if (size && (feof (fp) || ferror (fp))) { - cairo_status_t *error = png_get_error_ptr (png); - if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_READ_ERROR); - png_error (png, NULL); - } + status_ignored = _cairo_output_stream_destroy (png_closure->png_data); } + + return surface; } /** @@ -639,11 +718,11 @@ stdio_read_func (png_structp png, png_bytep data, png_size_t size) cairo_surface_t * cairo_image_surface_create_from_png (const char *filename) { - FILE *fp; + struct png_read_closure_t png_closure; cairo_surface_t *surface; - fp = fopen (filename, "rb"); - if (fp == NULL) { + png_closure.closure = fopen (filename, "rb"); + if (png_closure.closure == NULL) { cairo_status_t status; switch (errno) { case ENOMEM: @@ -659,32 +738,13 @@ cairo_image_surface_create_from_png (const char *filename) return _cairo_surface_create_in_error (status); } - surface = read_png (stdio_read_func, fp); - - fclose (fp); - - return surface; -} + png_closure.read_func = stdio_read_func; -struct png_read_closure_t { - cairo_read_func_t read_func; - void *closure; -}; + surface = read_png (&png_closure); -static void -stream_read_func (png_structp png, png_bytep data, png_size_t size) -{ - cairo_status_t status; - struct png_read_closure_t *png_closure; + fclose (png_closure.closure); - png_closure = png_get_io_ptr (png); - status = png_closure->read_func (png_closure->closure, data, size); - if (status) { - cairo_status_t *error = png_get_error_ptr (png); - if (*error == CAIRO_STATUS_SUCCESS) - *error = status; - png_error (png, NULL); - } + return surface; } /** @@ -717,5 +777,5 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, png_closure.read_func = read_func; png_closure.closure = closure; - return read_png (stream_read_func, &png_closure); + return read_png (&png_closure); } diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c index 1392bfa..202cb4d 100644 --- a/src/cairo-polygon.c +++ b/src/cairo-polygon.c @@ -39,6 +39,8 @@ void _cairo_polygon_init (cairo_polygon_t *polygon) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); + polygon->status = CAIRO_STATUS_SUCCESS; polygon->num_edges = 0; @@ -54,6 +56,8 @@ _cairo_polygon_fini (cairo_polygon_t *polygon) { if (polygon->edges != polygon->edges_embedded) free (polygon->edges); + + VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t))); } /* make room for at least one more edge */ @@ -64,6 +68,11 @@ _cairo_polygon_grow (cairo_polygon_t *polygon) int old_size = polygon->edges_size; int new_size = 4 * old_size; + if (CAIRO_INJECT_FAULT ()) { + polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + if (polygon->edges == polygon->edges_embedded) { new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t)); if (new_edges != NULL) @@ -73,7 +82,7 @@ _cairo_polygon_grow (cairo_polygon_t *polygon) new_size, sizeof (cairo_edge_t)); } - if (new_edges == NULL) { + if (unlikely (new_edges == NULL)) { polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } @@ -84,16 +93,17 @@ _cairo_polygon_grow (cairo_polygon_t *polygon) return TRUE; } -static void +void _cairo_polygon_add_edge (cairo_polygon_t *polygon, const cairo_point_t *p1, - const cairo_point_t *p2) + const cairo_point_t *p2, + int dir) { cairo_edge_t *edge; /* drop horizontal edges */ if (p1->y == p2->y) - goto DONE; + return; if (polygon->num_edges == polygon->edges_size) { if (! _cairo_polygon_grow (polygon)) @@ -104,15 +114,12 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon, if (p1->y < p2->y) { edge->edge.p1 = *p1; edge->edge.p2 = *p2; - edge->clockWise = 1; + edge->dir = dir; } else { edge->edge.p1 = *p2; edge->edge.p2 = *p1; - edge->clockWise = 0; + edge->dir = -dir; } - - DONE: - _cairo_polygon_move_to (polygon, p2); } void @@ -131,9 +138,9 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon, const cairo_point_t *point) { if (polygon->has_current_point) - _cairo_polygon_add_edge (polygon, &polygon->current_point, point); - else - _cairo_polygon_move_to (polygon, point); + _cairo_polygon_add_edge (polygon, &polygon->current_point, point, 1); + + _cairo_polygon_move_to (polygon, point); } void @@ -142,7 +149,8 @@ _cairo_polygon_close (cairo_polygon_t *polygon) if (polygon->has_current_point) { _cairo_polygon_add_edge (polygon, &polygon->current_point, - &polygon->first_point); + &polygon->first_point, + 1); polygon->has_current_point = FALSE; } diff --git a/src/cairo-private.h b/src/cairo-private.h index 43bd223..a60a6aa 100644 --- a/src/cairo-private.h +++ b/src/cairo-private.h @@ -48,7 +48,7 @@ struct _cairo { cairo_user_data_array_t user_data; cairo_gstate_t *gstate; - cairo_gstate_t gstate_tail[1]; + cairo_gstate_t gstate_tail[2]; cairo_gstate_t *gstate_freelist; cairo_path_fixed_t path[1]; diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h index e78833d..8567aff 100644 --- a/src/cairo-ps-surface-private.h +++ b/src/cairo-ps-surface-private.h @@ -67,8 +67,12 @@ typedef struct cairo_ps_surface { double height; int bbox_x1, bbox_y1, bbox_x2, bbox_y2; cairo_matrix_t cairo_to_ps; + + /* XXX These 3 are used as temporary storage whilst emitting patterns */ cairo_image_surface_t *image; + cairo_image_surface_t *acquired_image; void *image_extra; + cairo_bool_t use_string_datasource; cairo_bool_t current_pattern_is_solid_color; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index bfe424d..fde161d 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -63,6 +63,7 @@ #include "cairo-meta-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-type3-glyph-surface-private.h" +#include "cairo-image-info-private.h" #include <stdio.h> #include <ctype.h> @@ -79,6 +80,10 @@ static const cairo_surface_backend_t cairo_ps_surface_backend; static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; +static void +_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, + cairo_surface_pattern_t *pattern); + static const cairo_ps_level_t _cairo_ps_levels[] = { CAIRO_PS_LEVEL_2, @@ -257,7 +262,7 @@ _cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, snprintf (name, sizeof name, "f-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE); - if (status) + if (unlikely (status)) return status; /* FIXME: Figure out document structure convention for fonts */ @@ -288,7 +293,7 @@ _cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, snprintf (name, sizeof name, "f-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_fallback_init_hex (&subset, name, font_subset); - if (status) + if (unlikely (status)) return status; /* FIXME: Figure out document structure convention for fonts */ @@ -317,7 +322,7 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, unsigned int i, begin, end; status = _cairo_truetype_subset_init (&subset, font_subset); - if (status) + if (unlikely (status)) return status; /* FIXME: Figure out document structure convention for fonts */ @@ -330,14 +335,13 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->final_stream, "11 dict begin\n" "/FontType 42 def\n" - "/FontName /f-%d-%d def\n" + "/FontName /%s 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", - font_subset->font_id, - font_subset->subset_id); + subset.ps_name); /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ @@ -392,7 +396,9 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->final_stream, "] def\n" - "FontName currentdict end definefont pop\n"); + "/f-%d-%d currentdict end definefont pop\n", + font_subset->font_id, + font_subset->subset_id); _cairo_truetype_subset_fini (&subset); @@ -458,7 +464,7 @@ _cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_sub for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, font_subset->glyphs[i]); - if (status) + if (unlikely (status)) break; } @@ -499,6 +505,9 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, NULL, _cairo_ps_emit_imagemask, surface->font_subsets); + status = type3_surface->status; + if (unlikely (status)) + return status; for (i = 0; i < font_subset->num_glyphs; i++) { if (font_subset->glyph_names != NULL) { @@ -522,7 +531,7 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, font_subset->glyphs[i], &bbox, &width); - if (status) + if (unlikely (status)) break; _cairo_output_stream_printf (surface->final_stream, @@ -544,7 +553,7 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, } } cairo_surface_destroy (type3_surface); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->final_stream, @@ -577,7 +586,7 @@ _cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_su status = _cairo_scaled_font_subset_create_glyph_names (font_subset); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; #if CAIRO_HAS_FT_FONT @@ -606,7 +615,7 @@ _cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subs cairo_status_t status; status = _cairo_scaled_font_subset_create_glyph_names (font_subset); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset); @@ -630,19 +639,19 @@ _cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_ps_surface_analyze_user_font_subset, surface); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, _cairo_ps_surface_emit_unscaled_font_subset, surface); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, _cairo_ps_surface_emit_scaled_font_subset, surface); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, @@ -700,7 +709,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, cairo_ps_surface_t *surface; surface = malloc (sizeof (cairo_ps_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } @@ -725,11 +734,11 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); status = _cairo_output_stream_get_status (surface->stream); - if (status) + if (unlikely (status)) goto CLEANUP_OUTPUT_STREAM; surface->font_subsets = _cairo_scaled_font_subsets_create_simple (); - if (surface->font_subsets == NULL) { + if (unlikely (surface->font_subsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_OUTPUT_STREAM; } @@ -930,7 +939,7 @@ cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, cairo_status_t status; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -1005,7 +1014,7 @@ cairo_ps_surface_set_eps (cairo_surface_t *surface, cairo_status_t status; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -1030,7 +1039,7 @@ cairo_ps_surface_get_eps (cairo_surface_t *surface) cairo_status_t status; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return FALSE; } @@ -1064,7 +1073,7 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, cairo_status_t status; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -1077,7 +1086,7 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface, width_in_points, height_in_points); - if (status) + if (unlikely (status)) status = _cairo_surface_set_error (surface, status); } @@ -1178,7 +1187,7 @@ cairo_ps_surface_dsc_comment (cairo_surface_t *surface, char *comment_copy; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -1196,13 +1205,13 @@ cairo_ps_surface_dsc_comment (cairo_surface_t *surface, /* Then, copy the comment and store it in the appropriate array. */ comment_copy = strdup (comment); - if (comment_copy == NULL) { + if (unlikely (comment_copy == NULL)) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY); return; } status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy); - if (status) { + if (unlikely (status)) { free (comment_copy); status = _cairo_surface_set_error (surface, status); return; @@ -1232,7 +1241,7 @@ cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface) cairo_status_t status; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -1267,7 +1276,7 @@ cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) cairo_status_t status; status = _extract_ps_surface (surface, &ps_surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (surface, status); return; } @@ -1299,11 +1308,11 @@ _cairo_ps_surface_finish (void *abstract_surface) _cairo_ps_surface_emit_header (surface); status = _cairo_ps_surface_emit_font_subsets (surface); - if (status) + if (unlikely (status)) goto CLEANUP; status = _cairo_ps_surface_emit_body (surface); - if (status) + if (unlikely (status)) goto CLEANUP; _cairo_ps_surface_emit_footer (surface); @@ -1357,7 +1366,7 @@ _cairo_ps_surface_end_page (cairo_ps_surface_t *surface) cairo_int_status_t status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, @@ -1373,7 +1382,7 @@ _cairo_ps_surface_show_page (void *abstract_surface) cairo_int_status_t status; status = _cairo_ps_surface_end_page (surface); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "showpage\n"); @@ -1402,7 +1411,7 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (status) + if (unlikely (status)) return status; if (image->base.status) @@ -1437,7 +1446,7 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t } static cairo_bool_t -surface_pattern_supported (cairo_surface_pattern_t *pattern) +surface_pattern_supported (const cairo_surface_pattern_t *pattern) { cairo_extend_t extend; @@ -1478,9 +1487,9 @@ surface_pattern_supported (cairo_surface_pattern_t *pattern) static cairo_bool_t _gradient_pattern_supported (cairo_ps_surface_t *surface, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { - cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *) pattern; uint16_t alpha; cairo_extend_t extend; unsigned int i; @@ -1499,7 +1508,7 @@ _gradient_pattern_supported (cairo_ps_surface_t *surface, return FALSE; } - extend = cairo_pattern_get_extend (pattern); + extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); /* Radial gradients are currently only supported when one circle * is inside the other. */ @@ -1531,7 +1540,7 @@ _gradient_pattern_supported (cairo_ps_surface_t *surface, } static cairo_bool_t -pattern_supported (cairo_ps_surface_t *surface, cairo_pattern_t *pattern) +pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) { if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) return TRUE; @@ -1549,10 +1558,13 @@ pattern_supported (cairo_ps_surface_t *surface, cairo_pattern_t *pattern) static cairo_int_status_t _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { - if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { return CAIRO_INT_STATUS_UNSUPPORTED; + } if (! pattern_supported (surface, pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1564,8 +1576,12 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_meta (surface_pattern->surface)) - return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + if ( _cairo_surface_is_meta (surface_pattern->surface)) { + if (pattern->extend == CAIRO_EXTEND_PAD) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return CAIRO_INT_STATUS_ANALYZE_META_SURFACE_PATTERN; + } } if (op == CAIRO_OPERATOR_SOURCE) @@ -1597,7 +1613,7 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, static cairo_bool_t _cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { if (_cairo_ps_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) return TRUE; @@ -1721,13 +1737,14 @@ _string_array_stream_create (cairo_output_stream_t *output) string_array_stream_t *stream; stream = malloc (sizeof (string_array_stream_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _string_array_stream_write, + NULL, _string_array_stream_close); stream->output = output; stream->column = 0; @@ -1747,13 +1764,14 @@ _base85_array_stream_create (cairo_output_stream_t *output) string_array_stream_t *stream; stream = malloc (sizeof (string_array_stream_t)); - if (stream == NULL) { + if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _string_array_stream_write, + NULL, _string_array_stream_close); stream->output = output; stream->column = 0; @@ -1795,7 +1813,7 @@ _cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, background_color, 0, 0, image->width, image->height); - if (status) + if (unlikely (status)) goto fail; status = _cairo_surface_composite (CAIRO_OPERATOR_OVER, @@ -1807,7 +1825,7 @@ _cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, 0, 0, image->width, image->height); - if (status) + if (unlikely (status)) goto fail; _cairo_pattern_fini (&pattern.base); @@ -1824,7 +1842,7 @@ fail: static cairo_status_t _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, - unsigned char *data, + const unsigned char *data, unsigned long length, cairo_bool_t use_strings) { @@ -1837,12 +1855,12 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, string_array_stream = _base85_array_stream_create (surface->stream); status = _cairo_output_stream_get_status (string_array_stream); - if (status) + if (unlikely (status)) return _cairo_output_stream_destroy (string_array_stream); base85_stream = _cairo_base85_stream_create (string_array_stream); status = _cairo_output_stream_get_status (base85_stream); - if (status) { + if (unlikely (status)) { status2 = _cairo_output_stream_destroy (string_array_stream); return _cairo_output_stream_destroy (base85_stream); } @@ -1860,7 +1878,8 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, static cairo_status_t _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, cairo_image_surface_t *image, - cairo_operator_t op) + cairo_operator_t op, + cairo_filter_t filter) { cairo_status_t status; unsigned char *data, *data_compressed; @@ -1871,10 +1890,25 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, cairo_bool_t use_mask; uint32_t *pixel; int bit; + const char *interpolate; if (image->base.status) return image->base.status; + switch (filter) { + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + transparency = _cairo_image_analyze_transparency (image); /* PostScript can not represent the alpha channel, so we blend the @@ -1889,7 +1923,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, status = _cairo_ps_surface_flatten_image_transparency (surface, image, &opaque_image); - if (status) + if (unlikely (status)) return status; use_mask = FALSE; @@ -1911,7 +1945,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, data_size = image->height * image->width * 3; } data = malloc (data_size); - if (data == NULL) { + if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail1; } @@ -1960,7 +1994,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, * instead. */ data_compressed_size = data_size; data_compressed = _cairo_lzw_compress (data, &data_compressed_size); - if (data_compressed == NULL) { + if (unlikely (data_compressed == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail2; } @@ -1975,7 +2009,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, data_compressed, data_compressed_size, TRUE); - if (status) + if (unlikely (status)) goto bail3; _cairo_output_stream_printf (surface->stream, @@ -1995,10 +2029,12 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, " /ImageType 1 def\n" " /Width %d def\n" " /Height %d def\n" + " /Interpolate %s def\n" " /BitsPerComponent 8 def\n" " /Decode [ 0 1 0 1 0 1 ] def\n", image->width, - image->height); + image->height, + interpolate); if (surface->use_string_datasource) { _cairo_output_stream_printf (surface->stream, @@ -2021,6 +2057,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, " /ImageType 1 def\n" " /Width %d def\n" " /Height %d def\n" + " /Interpolate %s def\n" " /BitsPerComponent 1 def\n" " /Decode [ 1 0 ] def\n" " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" @@ -2030,6 +2067,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, image->height, image->width, image->height, + interpolate, image->height); } else { _cairo_output_stream_printf (surface->stream, @@ -2056,9 +2094,11 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, } _cairo_output_stream_printf (surface->stream, + " /Interpolate %s def\n" " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" "end\n" "image\n", + interpolate, opaque_image->height); } @@ -2087,6 +2127,94 @@ bail1: } static cairo_status_t +_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, + cairo_surface_t *source, + int width, + int height) +{ + cairo_status_t status; + const unsigned char *mime_data; + unsigned int mime_data_length; + cairo_image_info_t info; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.num_components != 1 && info.num_components != 3) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->use_string_datasource) { + /* 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, + "/CairoImageData [\n"); + + status = _cairo_ps_surface_emit_base85_string (surface, + mime_data, + mime_data_length, + TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/CairoImageDataIndex 0 def\n"); + } + + _cairo_output_stream_printf (surface->stream, + "/%s setcolorspace\n" + "8 dict dup begin\n" + " /ImageType 1 def\n" + " /Width %d def\n" + " /Height %d def\n" + " /BitsPerComponent %d def\n" + " /Decode [ 0 1 0 1 0 1 ] def\n", + info.num_components == 1 ? "DeviceGray" : "DeviceRGB", + info.width, + info.height, + info.bits_per_component); + + if (surface->use_string_datasource) { + _cairo_output_stream_printf (surface->stream, + " /DataSource {\n" + " CairoImageData CairoImageDataIndex get\n" + " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" + " CairoImageDataIndex CairoImageData length 1 sub gt\n" + " { /CairoImageDataIndex 0 def } if\n" + " } /ASCII85Decode filter /DCTDecode filter def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n"); + } + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" + "end\n" + "image\n", + info.height); + + if (!surface->use_string_datasource) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator. */ + status = _cairo_ps_surface_emit_base85_string (surface, + mime_data, + mime_data_length, + FALSE); + } + + return status; +} + +static cairo_status_t _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, cairo_surface_t *meta_surface) { @@ -2098,7 +2226,7 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, cairo_status_t status; status = _cairo_surface_get_extents (meta_surface, &meta_extents); - if (status) + if (unlikely (status)) return status; old_content = surface->content; @@ -2130,11 +2258,11 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, status = _cairo_meta_surface_replay_region (meta_surface, &surface->base, CAIRO_META_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, @@ -2146,7 +2274,7 @@ _cairo_ps_surface_emit_meta_surface (cairo_ps_surface_t *surface, _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; status = _cairo_surface_set_clip (&surface->base, old_clip); - if (status) + if (unlikely (status)) return status; _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, @@ -2200,40 +2328,100 @@ _cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, static cairo_status_t _cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents, int *width, int *height, - cairo_operator_t op) + int *origin_x, + int *origin_y) { cairo_status_t status; + cairo_surface_t *pad_image; + int x = 0; + int y = 0; + + surface->acquired_image = NULL; + surface->image = NULL; if (_cairo_surface_is_meta (pattern->surface)) { cairo_surface_t *meta_surface = pattern->surface; cairo_rectangle_int_t pattern_extents; status = _cairo_surface_get_extents (meta_surface, &pattern_extents); - if (status) + if (unlikely (status)) return status; *width = pattern_extents.width; *height = pattern_extents.height; } else { status = _cairo_surface_acquire_source_image (pattern->surface, - &surface->image, + &surface->acquired_image, &surface->image_extra); - if (status) + if (unlikely (status)) return status; + pad_image = &surface->acquired_image->base; + if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_pattern_t pad_pattern; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + x = -rect.x; + y = -rect.y; + + pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, + rect.width, + rect.height); + if (pad_image->status) { + status = pad_image->status; + goto BAIL; + } + + _cairo_pattern_init_for_surface (&pad_pattern, &surface->acquired_image->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, + &pad_pattern.base, + NULL, + pad_image, + 0, 0, + 0, 0, + 0, 0, + rect.width, + rect.height); + _cairo_pattern_fini (&pad_pattern.base); + if (unlikely (status)) { + if (pad_image != &surface->acquired_image->base) + cairo_surface_destroy (pad_image); + + goto BAIL; + } + } + + surface->image = (cairo_image_surface_t *) pad_image; *width = surface->image->width; *height = surface->image->height; + *origin_x = x; + *origin_y = y; } return CAIRO_STATUS_SUCCESS; + +BAIL: + _cairo_ps_surface_release_surface (surface, pattern); + + return status; } static cairo_status_t _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern, - cairo_operator_t op) + cairo_operator_t op, + int width, + int height) { cairo_status_t status; @@ -2243,7 +2431,15 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, status = _cairo_ps_surface_emit_meta_surface (surface, meta_surface); } else { - status = _cairo_ps_surface_emit_image (surface, surface->image, op); + if (cairo_pattern_get_extend (&pattern->base) != CAIRO_EXTEND_PAD) { + status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface, + width, height); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = _cairo_ps_surface_emit_image (surface, surface->image, + op, pattern->base.filter); } return status; @@ -2253,26 +2449,37 @@ static void _cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern) { - if (!_cairo_surface_is_meta (pattern->surface)) - _cairo_surface_release_source_image (pattern->surface, surface->image, + if (surface->image != surface->acquired_image) + cairo_surface_destroy (&surface->image->base); + + if (! _cairo_surface_is_meta (pattern->surface)) { + _cairo_surface_release_source_image (pattern->surface, + surface->acquired_image, surface->image_extra); + } + + surface->acquired_image = NULL; + surface->image = NULL; } static cairo_status_t _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents, cairo_operator_t op) { cairo_status_t status; int width, height; cairo_matrix_t cairo_p2d, ps_p2d; + int origin_x = 0; + int origin_y = 0; status = _cairo_ps_surface_acquire_surface (surface, pattern, - &width, - &height, - op); - if (status) + extents, + &width, &height, + &origin_x, &origin_y); + if (unlikely (status)) return status; cairo_p2d = pattern->base.matrix; @@ -2304,6 +2511,7 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ps_p2d = surface->cairo_to_ps; cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); + cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y); cairo_matrix_translate (&ps_p2d, 0.0, height); cairo_matrix_scale (&ps_p2d, 1.0, -1.0); @@ -2315,7 +2523,7 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ps_p2d.x0, ps_p2d.y0); } - status = _cairo_ps_surface_emit_surface (surface, pattern, op); + status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height); _cairo_ps_surface_release_surface (surface, pattern); return status; @@ -2324,6 +2532,7 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, static cairo_status_t _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents, cairo_operator_t op) { cairo_status_t status; @@ -2333,6 +2542,8 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, cairo_matrix_t cairo_p2d, ps_p2d; cairo_rectangle_int_t surface_extents; cairo_bool_t old_use_string_datasource; + int origin_x = 0; + int origin_y = 0; cairo_p2d = pattern->base.matrix; status = cairo_matrix_invert (&cairo_p2d); @@ -2341,19 +2552,19 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, ps_p2d = surface->cairo_to_ps; cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); + cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y); cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); cairo_matrix_scale (&ps_p2d, 1.0, -1.0); status = _cairo_ps_surface_acquire_surface (surface, pattern, - &pattern_width, - &pattern_height, - op); - if (status) + extents, + &pattern_width, &pattern_height, + &origin_x, &origin_y); + if (unlikely (status)) return status; switch (pattern->base.extend) { - /* We implement EXTEND_PAD like EXTEND_NONE for now */ case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { @@ -2410,8 +2621,9 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, xstep, ystep); } - status = _cairo_ps_surface_emit_surface (surface, pattern, op); - if (status) + status = _cairo_ps_surface_emit_surface (surface, pattern, op, + pattern_width, pattern_height); + if (unlikely (status)) return status; surface->use_string_datasource = old_use_string_datasource; @@ -2458,7 +2670,7 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, ">>\n"); status = _cairo_surface_get_extents (&surface->base, &surface_extents); - if (status) + if (unlikely (status)) return status; cairo_p2d = pattern->base.matrix; @@ -2558,7 +2770,7 @@ _cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, unsigned int i, n_stops; allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t)); - if (allstops == NULL) + if (unlikely (allstops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); stops = &allstops[1]; @@ -2764,7 +2976,7 @@ _cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base); - if (status) + if (unlikely (status)) return status; if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || @@ -2773,7 +2985,7 @@ _cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, &pattern->base, repeat_begin, repeat_end); - if (status) + if (unlikely (status)) return status; } @@ -2851,7 +3063,7 @@ _cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface, r2 = _cairo_fixed_to_double (pattern->r2); status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, @@ -2888,8 +3100,9 @@ _cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface, static cairo_status_t _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, - cairo_pattern_t *pattern, - cairo_operator_t op) + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op) { cairo_status_t status; @@ -2900,7 +3113,7 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, ! _cairo_color_equal (&surface->current_color, &solid->color)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); @@ -2914,7 +3127,7 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, surface->current_pattern_is_solid_color = FALSE; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; switch (pattern->type) { @@ -2926,22 +3139,23 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, case CAIRO_PATTERN_TYPE_SURFACE: status = _cairo_ps_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, + extents, op); - if (status) + if (unlikely (status)) return status; break; case CAIRO_PATTERN_TYPE_LINEAR: status = _cairo_ps_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); - if (status) + if (unlikely (status)) return status; break; case CAIRO_PATTERN_TYPE_RADIAL: status = _cairo_ps_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); - if (status) + if (unlikely (status)) return status; break; } @@ -2970,7 +3184,7 @@ _cairo_ps_surface_intersect_clip_path (void *abstract_surface, if (path == NULL) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (stream, "Q q\n"); @@ -3018,7 +3232,8 @@ _cairo_ps_surface_get_font_options (void *abstract_surface, static cairo_int_status_t _cairo_ps_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *paint_extents) { cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; @@ -3036,11 +3251,11 @@ _cairo_ps_surface_paint (void *abstract_surface, #endif status = _cairo_surface_get_extents (&surface->base, &extents); - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; if (source->type == CAIRO_PATTERN_TYPE_SURFACE && @@ -3053,17 +3268,17 @@ _cairo_ps_surface_paint (void *abstract_surface, status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) source, - op); - if (status) + paint_extents, op); + if (unlikely (status)) return status; _cairo_output_stream_printf (stream, "Q\n"); } else { - status = _cairo_ps_surface_emit_pattern (surface, source, op); + status = _cairo_ps_surface_emit_pattern (surface, source, paint_extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (stream, "0 0 %d %d rectfill\n", @@ -3077,13 +3292,14 @@ _cairo_ps_surface_paint (void *abstract_surface, static cairo_int_status_t _cairo_ps_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_ps_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -3098,10 +3314,13 @@ _cairo_ps_surface_stroke (void *abstract_surface, "%% _cairo_ps_surface_stroke\n"); #endif - status = _cairo_ps_surface_emit_pattern (surface, source, op); + status = _cairo_ps_surface_emit_pattern (surface, source, extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + return _cairo_pdf_operators_stroke (&surface->pdf_operators, path, style, @@ -3111,12 +3330,13 @@ _cairo_ps_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_ps_surface_fill (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_ps_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -3136,7 +3356,7 @@ _cairo_ps_surface_fill (void *abstract_surface, source->extend == CAIRO_EXTEND_PAD)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "q\n"); @@ -3144,23 +3364,23 @@ _cairo_ps_surface_fill (void *abstract_surface, status = _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); - if (status) + if (unlikely (status)) return status; status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) source, - op); - if (status) + extents, op); + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_pdf_operators_reset (&surface->pdf_operators); } else { - status = _cairo_ps_surface_emit_pattern (surface, source, op); + status = _cairo_ps_surface_emit_pattern (surface, source, extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; status = _cairo_pdf_operators_fill (&surface->pdf_operators, @@ -3174,11 +3394,12 @@ _cairo_ps_surface_fill (void *abstract_surface, static cairo_int_status_t _cairo_ps_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_ps_surface_t *surface = abstract_surface; cairo_status_t status; @@ -3196,11 +3417,11 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; - status = _cairo_ps_surface_emit_pattern (surface, source, op); + status = _cairo_ps_surface_emit_pattern (surface, source, extents, op); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; - if (status) + if (unlikely (status)) return status; return _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, @@ -3306,6 +3527,8 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* cairo_ps_surface_copy_page */ _cairo_ps_surface_show_page, NULL, /* set_clip_region */ diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 94cc327..91c1654 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -131,6 +131,79 @@ struct _cairo_quartz_font_face { * font face backend */ +static cairo_status_t +_cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + const char *family; + char *full_name; + CFStringRef cgFontName = NULL; + CGFontRef cgFont = NULL; + int loop; + + quartz_font_ensure_symbols(); + if (! _cairo_quartz_font_symbols_present) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + family = toy_face->family; + full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. + /* handle CSS-ish faces */ + if (!strcmp(family, "serif") || !strcmp(family, "Times Roman")) + family = "Times"; + else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans")) + family = "Helvetica"; + else if (!strcmp(family, "cursive")) + family = "Apple Chancery"; + else if (!strcmp(family, "fantasy")) + family = "Papyrus"; + else if (!strcmp(family, "monospace") || !strcmp(family, "mono")) + family = "Courier"; + + /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first, + * then drop the bold, then drop the slant, then drop both.. finally + * just use "Helvetica". And if Helvetica doesn't exist, give up. + */ + for (loop = 0; loop < 5; loop++) { + if (loop == 4) + family = "Helvetica"; + + strcpy (full_name, family); + + if (loop < 3 && (loop & 1) == 0) { + if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) + strcat (full_name, " Bold"); + } + + if (loop < 3 && (loop & 2) == 0) { + if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) + strcat (full_name, " Italic"); + else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) + strcat (full_name, " Oblique"); + } + + if (CGFontCreateWithFontNamePtr) { + cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); + cgFont = CGFontCreateWithFontNamePtr (cgFontName); + CFRelease (cgFontName); + } else { + cgFont = CGFontCreateWithNamePtr (full_name); + } + + if (cgFont) + break; + } + + if (!cgFont) { + /* Give up */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); + CGFontRelease (cgFont); + + return CAIRO_STATUS_SUCCESS; +} + static void _cairo_quartz_font_face_destroy (void *abstract_face) { @@ -139,6 +212,8 @@ _cairo_quartz_font_face_destroy (void *abstract_face) CGFontRelease (font_face->cgFont); } +static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; + static cairo_status_t _cairo_quartz_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, @@ -222,10 +297,10 @@ FINISH: return status; } -static const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { +const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { CAIRO_FONT_TYPE_QUARTZ, + _cairo_quartz_font_face_create_for_toy, _cairo_quartz_font_face_destroy, - NULL, /* direct implementation */ _cairo_quartz_font_face_scaled_font_create }; @@ -277,130 +352,8 @@ _cairo_quartz_scaled_to_face (void *abstract_font) return (cairo_quartz_font_face_t*) font_face; } -static cairo_status_t -_cairo_quartz_font_get_implementation (cairo_toy_font_face_t *toy_face, - cairo_scaled_font_t **font_face_out) -{ - static cairo_user_data_key_t impl_font_face_key; - cairo_font_face_t *face; - cairo_status_t status; - const char *family = toy_face->family; - char *full_name = malloc(strlen(family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. - CFStringRef cgFontName = NULL; - CGFontRef cgFont = NULL; - int loop; - - face = cairo_font_face_get_user_data (&toy_face->base, - &impl_font_face_key); - if (face) { - *font_face_out = face; - return CAIRO_STATUS_SUCCESS; - } - - quartz_font_ensure_symbols(); - if (! _cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* handle CSS-ish faces */ - if (!strcmp(family, "serif") || !strcmp(family, "Times Roman")) - family = "Times"; - else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans")) - family = "Helvetica"; - else if (!strcmp(family, "cursive")) - family = "Apple Chancery"; - else if (!strcmp(family, "fantasy")) - family = "Papyrus"; - else if (!strcmp(family, "monospace") || !strcmp(family, "mono")) - family = "Courier"; - - /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first, - * then drop the bold, then drop the slant, then drop both.. finally - * just use "Helvetica". And if Helvetica doesn't exist, give up. - */ - for (loop = 0; loop < 5; loop++) { - if (loop == 4) - family = "Helvetica"; - - strcpy (full_name, family); - - if (loop < 3 && (loop & 1) == 0) { - if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) - strcat (full_name, " Bold"); - } - - if (loop < 3 && (loop & 2) == 0) { - if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) - strcat (full_name, " Italic"); - else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) - strcat (full_name, " Oblique"); - } - - if (CGFontCreateWithFontNamePtr) { - cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - cgFont = CGFontCreateWithFontNamePtr (cgFontName); - CFRelease (cgFontName); - } else { - cgFont = CGFontCreateWithNamePtr (full_name); - } - - if (cgFont) - break; - } - - if (!cgFont) { - /* Give up */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - face = cairo_quartz_font_face_create_for_cgfont (cgFont); - CGFontRelease (cgFont); - - if (face->status) - return face->status; - - status = cairo_font_face_set_user_data (&toy_face->base, - &impl_font_face_key, - face, - (cairo_destroy_func_t) cairo_font_face_destroy); - - if (status) { - cairo_font_face_destroy (face); - return status; - } - - *font_face_out = face; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_quartz_font_create_toy (cairo_toy_font_face_t *toy_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font_out) -{ - cairo_font_face_t *face; - cairo_scaled_font_t *scaled_font; - cairo_status_t status; - - status = _cairo_quartz_font_get_implementation (toy_face, &face); - if (status) - return status; - - status = _cairo_quartz_font_face_scaled_font_create (face, - font_matrix, ctm, - options, - &scaled_font); - cairo_font_face_destroy (face); - if (status) - return status; - - *font_out = scaled_font; - return CAIRO_STATUS_SUCCESS; -} - static void -_cairo_quartz_font_fini(void *abstract_font) +_cairo_quartz_scaled_font_fini(void *abstract_font) { } @@ -740,9 +693,9 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, } static cairo_int_status_t -_cairo_quartz_font_scaled_glyph_init (void *abstract_font, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_glyph_info_t info) +_cairo_quartz_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; @@ -773,12 +726,10 @@ _cairo_quartz_ucs4_to_index (void *abstract_font, return glyph; } -const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { +static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { CAIRO_FONT_TYPE_QUARTZ, - _cairo_quartz_font_get_implementation, - _cairo_quartz_font_create_toy, - _cairo_quartz_font_fini, - _cairo_quartz_font_scaled_glyph_init, + _cairo_quartz_scaled_font_fini, + _cairo_quartz_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_quartz_ucs4_to_index, NULL, /* show_glyphs */ diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c index 3eee2c1..dab3dc8 100644 --- a/src/cairo-quartz-image-surface.c +++ b/src/cairo-quartz-image-surface.c @@ -41,6 +41,7 @@ #define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY))) #define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH))) +#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE))) #define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT))) static void @@ -163,6 +164,8 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ @@ -225,10 +228,10 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) data = image_surface->data; if (!_cairo_quartz_verify_surface_size(width, height)) - return SURFACE_ERROR_NO_MEMORY; + return SURFACE_ERROR_INVALID_SIZE; if (width == 0 || height == 0) - return SURFACE_ERROR_NO_MEMORY; + return SURFACE_ERROR_INVALID_SIZE; if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24) return SURFACE_ERROR_INVALID_FORMAT; diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 163c9fb..892a156 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -34,12 +34,17 @@ * Vladimir Vukicevic <vladimir@mozilla.com> */ +#define _GNU_SOURCE /* required for RTLD_DEFAULT */ #include "cairoint.h" #include "cairo-quartz-private.h" #include <dlfcn.h> +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT ((void *) 0) +#endif + /* The 10.5 SDK includes a funky new definition of FloatToFixed which * causes all sorts of breakage; so reset to old-style definition */ @@ -289,7 +294,8 @@ typedef struct _quartz_stroke { /* cairo path -> execute in context */ static cairo_status_t -_cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point) +_cairo_path_to_quartz_context_move_to (void *closure, + const cairo_point_t *point) { //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); quartz_stroke_t *stroke = (quartz_stroke_t *)closure; @@ -304,7 +310,8 @@ _cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point) +_cairo_path_to_quartz_context_line_to (void *closure, + const cairo_point_t *point) { //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); quartz_stroke_t *stroke = (quartz_stroke_t *)closure; @@ -322,7 +329,10 @@ _cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2) +_cairo_path_to_quartz_context_curve_to (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) { //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n", // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), @@ -631,7 +641,7 @@ static void ComputeGradientValue (void *info, const float *in, float *out) { double fdist = *in; - cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; + const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; unsigned int i; /* Put fdist back in the 0.0..1.0 range if we're doing @@ -680,7 +690,7 @@ ComputeGradientValue (void *info, const float *in, float *out) } static CGFunctionRef -CreateGradientFunction (cairo_gradient_pattern_t *gpat) +CreateGradientFunction (const cairo_gradient_pattern_t *gpat) { cairo_pattern_t *pat; float input_value_range[2] = { 0.f, 1.f }; @@ -704,7 +714,7 @@ CreateGradientFunction (cairo_gradient_pattern_t *gpat) static CGFunctionRef CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface, - cairo_gradient_pattern_t *gpat, + const cairo_gradient_pattern_t *gpat, CGPoint *start, CGPoint *end, CGAffineTransform matrix) { @@ -916,7 +926,7 @@ SurfacePatternReleaseInfoFunc (void *ainfo) static cairo_int_status_t _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest, - cairo_pattern_t *apattern, + const cairo_pattern_t *apattern, CGPatternRef *cgpat) { cairo_surface_pattern_t *spattern; @@ -1025,7 +1035,7 @@ typedef enum { static cairo_quartz_action_t _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, - cairo_pattern_t *source) + const cairo_pattern_t *source) { CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext); CGAffineTransform ctm; @@ -1034,6 +1044,7 @@ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, cairo_surface_t *fallback; cairo_t *fallback_cr; CGImageRef img; + cairo_pattern_t *source_copy; cairo_status_t status; @@ -1062,7 +1073,13 @@ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, /* Paint the source onto our temporary */ fallback_cr = cairo_create (fallback); cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source (fallback_cr, source); + + /* Use a copy of the pattern because it is const and could be allocated + * on the stack */ + status = _cairo_pattern_create_copy (&source_copy, source); + cairo_set_source (fallback_cr, source_copy); + cairo_pattern_destroy (source_copy); + cairo_paint (fallback_cr); cairo_destroy (fallback_cr); @@ -1082,9 +1099,9 @@ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, static cairo_quartz_action_t _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, - cairo_linear_pattern_t *lpat) + const cairo_linear_pattern_t *lpat) { - cairo_pattern_t *abspat = (cairo_pattern_t *) lpat; + const cairo_pattern_t *abspat = &lpat->base.base; cairo_matrix_t mat; CGPoint start, end; CGFunctionRef gradFunc; @@ -1097,7 +1114,7 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, return DO_SOLID; } - cairo_pattern_get_matrix (abspat, &mat); + mat = abspat->matrix; cairo_matrix_invert (&mat); _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform); @@ -1108,16 +1125,13 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x), _cairo_fixed_to_double (lpat->p2.y)); - // ref will be released by the CGShading's destructor - cairo_pattern_reference ((cairo_pattern_t*) lpat); - if (abspat->extend == CAIRO_EXTEND_NONE || abspat->extend == CAIRO_EXTEND_PAD) { - gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat); + gradFunc = CreateGradientFunction (&lpat->base); } else { gradFunc = CreateRepeatingGradientFunction (surface, - (cairo_gradient_pattern_t*) lpat, + &lpat->base, &start, &end, surface->sourceTransform); } @@ -1134,9 +1148,9 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, static cairo_quartz_action_t _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, - cairo_radial_pattern_t *rpat) + const cairo_radial_pattern_t *rpat) { - cairo_pattern_t *abspat = (cairo_pattern_t *)rpat; + const cairo_pattern_t *abspat = &rpat->base.base; cairo_matrix_t mat; CGPoint start, end; CGFunctionRef gradFunc; @@ -1157,10 +1171,10 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, * Radial shadings). So, instead, let's just render an image * for pixman to draw the shading into, and use that. */ - return _cairo_quartz_setup_fallback_source (surface, (cairo_pattern_t*) rpat); + return _cairo_quartz_setup_fallback_source (surface, &rpat->base.base); } - cairo_pattern_get_matrix (abspat, &mat); + mat = abspat->matrix; cairo_matrix_invert (&mat); _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform); @@ -1171,10 +1185,7 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x), _cairo_fixed_to_double (rpat->c2.y)); - // ref will be released by the CGShading's destructor - cairo_pattern_reference ((cairo_pattern_t*) rpat); - - gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat); + gradFunc = CreateGradientFunction (&rpat->base); surface->sourceShading = CGShadingCreateRadial (rgb, start, @@ -1192,7 +1203,7 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, static cairo_quartz_action_t _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, - cairo_pattern_t *source) + const cairo_pattern_t *source) { assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern)); @@ -1217,13 +1228,13 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, } if (source->type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *)source; + const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source; return _cairo_quartz_setup_linear_source (surface, lpat); } if (source->type == CAIRO_PATTERN_TYPE_RADIAL) { - cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *)source; + const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source; return _cairo_quartz_setup_radial_source (surface, rpat); } @@ -1231,7 +1242,7 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, if (source->type == CAIRO_PATTERN_TYPE_SURFACE && (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) { - cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source; + const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; cairo_surface_t *pat_surf = spat->surface; CGImageRef img; cairo_matrix_t m = spat->base.matrix; @@ -1338,7 +1349,7 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, static void _cairo_quartz_teardown_source (cairo_quartz_surface_t *surface, - cairo_pattern_t *source) + const cairo_pattern_t *source) { CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality); @@ -1511,6 +1522,23 @@ _cairo_quartz_surface_acquire_source_image (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } +static cairo_surface_t * +_cairo_quartz_surface_snapshot (void *abstract_surface) +{ + cairo_int_status_t status; + cairo_quartz_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + + if (surface->imageSurfaceEquiv) + return NULL; + + status = _cairo_quartz_get_image (surface, &image); + if (unlikely (status)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + return &image->base; +} + static void _cairo_quartz_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, @@ -1578,7 +1606,7 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, // verify width and height of surface if (!_cairo_quartz_verify_surface_size(width, height)) { return _cairo_surface_create_in_error (_cairo_error - (CAIRO_STATUS_NO_MEMORY)); + (CAIRO_STATUS_INVALID_SIZE)); } return cairo_quartz_surface_create (format, width, height); @@ -1587,6 +1615,7 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, static cairo_status_t _cairo_quartz_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -1685,7 +1714,8 @@ _cairo_quartz_surface_get_extents (void *abstract_surface, static cairo_int_status_t _cairo_quartz_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -1736,11 +1766,12 @@ _cairo_quartz_surface_paint (void *abstract_surface, static cairo_int_status_t _cairo_quartz_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -1841,13 +1872,14 @@ _cairo_quartz_surface_fill (void *abstract_surface, static cairo_int_status_t _cairo_quartz_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -1987,11 +2019,12 @@ _cairo_quartz_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_quartz_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { CGAffineTransform textTransform, ctm; #define STATIC_BUF_SIZE 64 @@ -2196,22 +2229,23 @@ BAIL: static cairo_int_status_t _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_surface_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_surface_pattern_t *mask, + cairo_rectangle_int_t *extents) { - cairo_rectangle_int_t extents; + cairo_rectangle_int_t mask_extents; CGRect rect; CGImageRef img; cairo_surface_t *pat_surf = mask->surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; CGAffineTransform ctm, mask_matrix; - status = _cairo_surface_get_extents (pat_surf, &extents); + status = _cairo_surface_get_extents (pat_surf, &mask_extents); if (status) return status; // everything would be masked out, so do nothing - if (extents.width == 0 || extents.height == 0) + if (mask_extents.width == 0 || mask_extents.height == 0) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img); @@ -2220,7 +2254,7 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, if (status) return status; - rect = CGRectMake (0.0f, 0.0f, extents.width, extents.height); + rect = CGRectMake (0.0f, 0.0f, mask_extents.width, mask_extents.height); CGContextSaveGState (surface->cgContext); @@ -2237,7 +2271,7 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, CGContextSetCTM (surface->cgContext, ctm); - status = _cairo_quartz_surface_paint (surface, op, source); + status = _cairo_quartz_surface_paint (surface, op, source, extents); CGContextRestoreGState (surface->cgContext); @@ -2261,8 +2295,9 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, static cairo_int_status_t _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { int width = surface->extents.width - surface->extents.x; int height = surface->extents.height - surface->extents.y; @@ -2271,6 +2306,7 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, cairo_t *gradient_surf_cr = NULL; cairo_surface_pattern_t surface_pattern; + cairo_pattern_t *mask_copy; cairo_int_status_t status; /* Render the gradient to a surface */ @@ -2278,7 +2314,13 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, width, height); gradient_surf_cr = cairo_create(gradient_surf); - cairo_set_source (gradient_surf_cr, mask); + + /* make a copy of the pattern because because cairo_set_source doesn't take + * a 'const cairo_pattern_t *' */ + _cairo_pattern_create_copy (&mask_copy, mask); + cairo_set_source (gradient_surf_cr, mask_copy); + cairo_pattern_destroy (mask_copy); + cairo_set_operator (gradient_surf_cr, CAIRO_OPERATOR_SOURCE); cairo_paint (gradient_surf_cr); status = cairo_status (gradient_surf_cr); @@ -2289,7 +2331,7 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf); - status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern); + status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, extents); _cairo_pattern_fini (&surface_pattern.base); @@ -2303,8 +2345,9 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, static cairo_int_status_t _cairo_quartz_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; @@ -2319,7 +2362,7 @@ _cairo_quartz_surface_mask (void *abstract_surface, cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); - rv = _cairo_quartz_surface_paint (surface, op, source); + rv = _cairo_quartz_surface_paint (surface, op, source, extents); CGContextSetAlpha (surface->cgContext, 1.0); return rv; @@ -2329,9 +2372,9 @@ _cairo_quartz_surface_mask (void *abstract_surface, if (CGContextClipToMaskPtr) { /* For these, we can skip creating a temporary surface, since we already have one */ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) - return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask); + return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, extents); - return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask); + return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, extents); } /* So, CGContextClipToMask is not present in 10.3.9, so we're @@ -2408,6 +2451,8 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ @@ -2430,7 +2475,7 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = { NULL, /* show_glyphs */ #endif - NULL, /* snapshot */ + _cairo_quartz_surface_snapshot, NULL, /* is_similar */ NULL, /* reset */ NULL /* fill_stroke */ @@ -2559,7 +2604,7 @@ cairo_quartz_surface_create (cairo_format_t format, // verify width and height of surface if (!_cairo_quartz_verify_surface_size(width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (width == 0 || height == 0) { return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c index 2143f0c..b139624 100644 --- a/src/cairo-rectangle.c +++ b/src/cairo-rectangle.c @@ -153,8 +153,8 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) cairo_fixed_t xlen, ylen; - if (_cairo_box_contains_point(box, &line->p1) || - _cairo_box_contains_point(box, &line->p2)) + if (_cairo_box_contains_point (box, &line->p1) || + _cairo_box_contains_point (box, &line->p2)) return TRUE; xlen = P2x - P1x; @@ -216,10 +216,31 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) } cairo_bool_t -_cairo_box_contains_point (cairo_box_t *box, cairo_point_t *point) +_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point) { if (point->x < box->p1.x || point->x > box->p2.x || point->y < box->p1.y || point->y > box->p2.y) return FALSE; return TRUE; } + +void +_cairo_composite_rectangles_init( + cairo_composite_rectangles_t *rects, + int all_x, + int all_y, + int width, + int height) +{ + rects->src.x = all_x; + rects->src.y = all_y; + rects->mask.x = all_x; + rects->mask.y = all_y; + rects->clip.x = all_x; + rects->clip.y = all_y; + rects->dst.x = all_x; + rects->dst.y = all_y; + + rects->width = width; + rects->height = height; +} diff --git a/src/cairo-region-private.h b/src/cairo-region-private.h deleted file mode 100644 index 588762e..0000000 --- a/src/cairo-region-private.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ -/* Cairo - a vector graphics library with display and print output - * - * Copyright © 2007 Mozilla Corporation - * - * 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 Mozilla Corporation - * - * Contributor(s): - * Vladimir Vukicevic <vladimir@pobox.com> - */ - -#ifndef CAIRO_REGION_PRIVATE_H -#define CAIRO_REGION_PRIVATE_H - -#include "cairo-compiler-private.h" -#include "cairo-types-private.h" - -#include <pixman.h> - -CAIRO_BEGIN_DECLS - -/* #cairo_region_t is defined in cairoint.h */ - -struct _cairo_region { - pixman_region32_t rgn; -}; - -cairo_private void -_cairo_region_init (cairo_region_t *region); - -cairo_private void -_cairo_region_init_rect (cairo_region_t *region, - cairo_rectangle_int_t *rect); - -cairo_private cairo_int_status_t -_cairo_region_init_boxes (cairo_region_t *region, - cairo_box_int_t *boxes, - int count); - -cairo_private void -_cairo_region_fini (cairo_region_t *region); - -cairo_private cairo_int_status_t -_cairo_region_copy (cairo_region_t *dst, - cairo_region_t *src); - -cairo_private int -_cairo_region_num_boxes (cairo_region_t *region); - -cairo_private cairo_int_status_t -_cairo_region_get_boxes (cairo_region_t *region, - int *num_boxes, - cairo_box_int_t **boxes); - -cairo_private void -_cairo_region_boxes_fini (cairo_region_t *region, - cairo_box_int_t *boxes); - -cairo_private void -_cairo_region_get_extents (cairo_region_t *region, - cairo_rectangle_int_t *extents); - -cairo_private cairo_int_status_t -_cairo_region_subtract (cairo_region_t *dst, - cairo_region_t *a, - cairo_region_t *b); - -cairo_private cairo_int_status_t -_cairo_region_intersect (cairo_region_t *dst, - cairo_region_t *a, - cairo_region_t *b); - -cairo_private cairo_int_status_t -_cairo_region_union_rect (cairo_region_t *dst, - cairo_region_t *src, - cairo_rectangle_int_t *rect); - -cairo_private cairo_bool_t -_cairo_region_not_empty (cairo_region_t *region); - -cairo_private void -_cairo_region_translate (cairo_region_t *region, - int x, int y); - -cairo_private pixman_region_overlap_t -_cairo_region_contains_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *box); - - -CAIRO_END_DECLS - -#endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/src/cairo-region.c b/src/cairo-region.c index 23a042f..c355b4a 100644 --- a/src/cairo-region.c +++ b/src/cairo-region.c @@ -33,189 +33,583 @@ * Contributor(s): * Owen Taylor <otaylor@redhat.com> * Vladimir Vukicevic <vladimir@pobox.com> + * Søren Sandmann <sandmann@daimi.au.dk> */ #include "cairoint.h" +static const cairo_region_t _cairo_region_nil = { + CAIRO_STATUS_NO_MEMORY, /* status */ +}; + +/** + * _cairo_region_set_error: + * @region: a region + * @status: a status value indicating an error + * + * Atomically sets region->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal + * status values. + * + * All assignments of an error status to region->status should happen + * through _cairo_region_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the + * nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +static cairo_status_t +_cairo_region_set_error (cairo_region_t *region, + cairo_status_t status) +{ + if (! _cairo_status_is_error (status)) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (®ion->status, status); + + return _cairo_error (status); +} + void _cairo_region_init (cairo_region_t *region) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); + + region->status = CAIRO_STATUS_SUCCESS; pixman_region32_init (®ion->rgn); } void -_cairo_region_init_rect (cairo_region_t *region, - cairo_rectangle_int_t *rect) +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); + + region->status = CAIRO_STATUS_SUCCESS; pixman_region32_init_rect (®ion->rgn, - rect->x, rect->y, - rect->width, rect->height); + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); } -cairo_int_status_t -_cairo_region_init_boxes (cairo_region_t *region, - cairo_box_int_t *boxes, - int count) +void +_cairo_region_fini (cairo_region_t *region) { - pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; - pixman_box32_t *pboxes = stack_pboxes; - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - int i; + pixman_region32_fini (®ion->rgn); + VG (VALGRIND_MAKE_MEM_NOACCESS (region, sizeof (cairo_region_t))); +} - if (count > ARRAY_LENGTH(stack_pboxes)) { - pboxes = _cairo_malloc_ab (count, sizeof(pixman_box32_t)); - if (pboxes == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } +/** + * cairo_region_create: + * + * Allocates a new empty region object. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create (void) +{ + cairo_region_t *region; - for (i = 0; i < count; i++) { - pboxes[i].x1 = boxes[i].p1.x; - pboxes[i].y1 = boxes[i].p1.y; - pboxes[i].x2 = boxes[i].p2.x; - pboxes[i].y2 = boxes[i].p2.y; - } + region = _cairo_malloc (sizeof (cairo_region_t)); + if (region == NULL) + return (cairo_region_t *) &_cairo_region_nil; - if (!pixman_region32_init_rects (®ion->rgn, pboxes, count)) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + region->status = CAIRO_STATUS_SUCCESS; - if (pboxes != stack_pboxes) - free (pboxes); + pixman_region32_init (®ion->rgn); - return status; + return region; } +slim_hidden_def (cairo_region_create); -void -_cairo_region_fini (cairo_region_t *region) +/** + * cairo_region_create_rectangle: + * @rectangle: a #cairo_rectangle_int_t + * + * Allocates a new region object containing @rectangle. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) { - pixman_region32_fini (®ion->rgn); -} + cairo_region_t *region; -cairo_int_status_t -_cairo_region_copy (cairo_region_t *dst, cairo_region_t *src) -{ - if (!pixman_region32_copy (&dst->rgn, &src->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + region = _cairo_malloc (sizeof (cairo_region_t)); + if (region == NULL) + return (cairo_region_t *) &_cairo_region_nil; - return CAIRO_STATUS_SUCCESS; -} + region->status = CAIRO_STATUS_SUCCESS; -int -_cairo_region_num_boxes (cairo_region_t *region) -{ - return pixman_region32_n_rects (®ion->rgn); + pixman_region32_init_rect (®ion->rgn, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + return region; } +slim_hidden_def (cairo_region_create_rectangle); -cairo_int_status_t -_cairo_region_get_boxes (cairo_region_t *region, int *num_boxes, cairo_box_int_t **boxes) +/** + * cairo_region_copy: + * @original: a #cairo_region_t + * + * Allocates a new region object copying the area from @original. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_copy (cairo_region_t *original) { - int nboxes; - pixman_box32_t *pboxes; - cairo_box_int_t *cboxes; - int i; + cairo_region_t *copy; - pboxes = pixman_region32_rectangles (®ion->rgn, &nboxes); + if (original->status) + return (cairo_region_t *) &_cairo_region_nil; - if (nboxes == 0) { - *num_boxes = 0; - *boxes = NULL; - return CAIRO_STATUS_SUCCESS; + copy = cairo_region_create (); + if (copy->status) + return copy; + + if (! pixman_region32_copy (©->rgn, &original->rgn)) { + cairo_region_destroy (copy); + return (cairo_region_t *) &_cairo_region_nil; } - cboxes = _cairo_malloc_ab (nboxes, sizeof(cairo_box_int_t)); - if (cboxes == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return copy; +} +slim_hidden_def (cairo_region_copy); - for (i = 0; i < nboxes; i++) { - cboxes[i].p1.x = pboxes[i].x1; - cboxes[i].p1.y = pboxes[i].y1; - cboxes[i].p2.x = pboxes[i].x2; - cboxes[i].p2.y = pboxes[i].y2; - } +/** + * cairo_region_destroy: + * @region: a #cairo_region_t + * + * Destroys a #cairo_region_t object created with + * cairo_region_create(), cairo_region_copy(), or + * or cairo_region_create_rectangle(). + * + * Since: 1.10 + **/ +void +cairo_region_destroy (cairo_region_t *region) +{ + if (region == (cairo_region_t *) &_cairo_region_nil) + return; - *num_boxes = nboxes; - *boxes = cboxes; + pixman_region32_fini (®ion->rgn); + free (region); +} +slim_hidden_def (cairo_region_destroy); - return CAIRO_STATUS_SUCCESS; +/** + * cairo_region_num_rectangles: + * @region: a #cairo_region_t + * + * Returns the number of rectangles contained in @region. + * + * Return value: The number of rectangles contained in @region. + * + * Since: 1.10 + **/ +int +cairo_region_num_rectangles (cairo_region_t *region) +{ + if (region->status) + return 0; + + return pixman_region32_n_rects (®ion->rgn); } +slim_hidden_def (cairo_region_num_rectangles); +/** + * cairo_region_get_rectangle: + * @region: a #cairo_region_t + * @nth: a number indicating which rectangle should be returned + * @rectangle: return location for a #cairo_rectangle_int_t + * + * Stores the @nth rectangle from the region in @rectangle. + * + * Since: 1.10 + **/ void -_cairo_region_boxes_fini (cairo_region_t *region, cairo_box_int_t *boxes) +cairo_region_get_rectangle (cairo_region_t *region, + int nth, + cairo_rectangle_int_t *rectangle) { - free (boxes); + pixman_box32_t *pbox; + + if (region->status) { + rectangle->x = rectangle->y = 0; + rectangle->width = rectangle->height = 0; + return; + } + + pbox = pixman_region32_rectangles (®ion->rgn, NULL) + nth; + + rectangle->x = pbox->x1; + rectangle->y = pbox->y1; + rectangle->width = pbox->x2 - pbox->x1; + rectangle->height = pbox->y2 - pbox->y1; } +slim_hidden_def (cairo_region_get_rectangle); /** - * _cairo_region_get_extents: + * cairo_region_get_extents: * @region: a #cairo_region_t - * @rect: rectangle into which to store the extents + * @rectangle: rectangle into which to store the extents + * + * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t * - * Gets the bounding box of a region as a #cairo_rectangle_int_t + * Since: 1.10 **/ void -_cairo_region_get_extents (cairo_region_t *region, cairo_rectangle_int_t *extents) +cairo_region_get_extents (cairo_region_t *region, + cairo_rectangle_int_t *extents) { - pixman_box32_t *pextents = pixman_region32_extents (®ion->rgn); + pixman_box32_t *pextents; + + if (region->status) { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return; + } + + pextents = pixman_region32_extents (®ion->rgn); extents->x = pextents->x1; extents->y = pextents->y1; extents->width = pextents->x2 - pextents->x1; extents->height = pextents->y2 - pextents->y1; } +slim_hidden_def (cairo_region_get_extents); -cairo_int_status_t -_cairo_region_subtract (cairo_region_t *dst, cairo_region_t *a, cairo_region_t *b) +/** + * cairo_region_status: + * @region: a #cairo_region_t + * + * Checks whether an error has previous occured for this + * region object. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_status (cairo_region_t *region) { - if (!pixman_region32_subtract (&dst->rgn, &a->rgn, &b->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return region->status; +} +slim_hidden_def (cairo_region_status); + +/** + * cairo_region_subtract: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Subtracts @other from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_region_subtract); -cairo_int_status_t -_cairo_region_intersect (cairo_region_t *dst, cairo_region_t *a, cairo_region_t *b) +/** + * cairo_region_subtract_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Subtracts @rectangle from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) { - if (!pixman_region32_intersect (&dst->rgn, &a->rgn, &b->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_subtract_rectangle); + +/** + * cairo_region_intersect: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the intersection of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect (cairo_region_t *dst, cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_region_intersect); -cairo_int_status_t -_cairo_region_union_rect (cairo_region_t *dst, - cairo_region_t *src, - cairo_rectangle_int_t *rect) +/** + * cairo_region_intersect_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the intersection of @dst with @rectangle and places the + * result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) { - if (!pixman_region32_union_rect (&dst->rgn, &src->rgn, - rect->x, rect->y, - rect->width, rect->height)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_intersect_rectangle); + +/** + * cairo_region_union: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the union of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union (cairo_region_t *dst, + cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_region_union); + +/** + * cairo_region_union_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the union of @dst with @rectangle and places the result in @dst. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_union_rectangle); + +/** + * cairo_region_is_empty: + * @region: a #cairo_region_t + * + * Checks whether @region is empty. + * + * Return value: %TRUE if @region is empty, %FALSE if it isn't. + * + * Since: 1.10 + **/ cairo_bool_t -_cairo_region_not_empty (cairo_region_t *region) +cairo_region_is_empty (cairo_region_t *region) { - return (cairo_bool_t) pixman_region32_not_empty (®ion->rgn); + if (region->status) + return TRUE; + + return ! pixman_region32_not_empty (®ion->rgn); } +slim_hidden_def (cairo_region_is_empty); +/** + * cairo_region_translate: + * @region: a #cairo_region_t + * @dx: Amount to translate in the x direction + * @dy: Amount to translate in the y direction + * + * Translates @region by (@dx, @dy). + * + * Since: 1.10 + **/ void -_cairo_region_translate (cairo_region_t *region, - int x, int y) +cairo_region_translate (cairo_region_t *region, + int dx, int dy) { - pixman_region32_translate (®ion->rgn, x, y); + if (region->status) + return; + + pixman_region32_translate (®ion->rgn, dx, dy); } +slim_hidden_def (cairo_region_translate); -pixman_region_overlap_t -_cairo_region_contains_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *rect) +/** + * cairo_region_contains_rectangle: + * @region: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Checks whether @rectangle is inside, outside or partially contained + * in @region + * + * Return value: + * %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region, + * %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or + * %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region. + * + * Since: 1.10 + **/ +cairo_region_overlap_t +cairo_region_contains_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) { pixman_box32_t pbox; + pixman_region_overlap_t poverlap; + + if (region->status) + return CAIRO_REGION_OVERLAP_OUT; + + pbox.x1 = rectangle->x; + pbox.y1 = rectangle->y; + pbox.x2 = rectangle->x + rectangle->width; + pbox.y2 = rectangle->y + rectangle->height; + + poverlap = pixman_region32_contains_rectangle (®ion->rgn, &pbox); + switch (poverlap) { + default: + case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; + case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN; + case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART; + } +} +slim_hidden_def (cairo_region_contains_rectangle); - pbox.x1 = rect->x; - pbox.y1 = rect->y; - pbox.x2 = rect->x + rect->width; - pbox.y2 = rect->y + rect->height; +/** + * cairo_region_contains_point: + * @region: a #cairo_region_t + * @x: the x coordinate of a point + * @y: the y coordinate of a point + * + * Checks whether (@x, @y) is contained in @region. + * + * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_contains_point (cairo_region_t *region, + int x, int y) +{ + pixman_box32_t box; + + if (region->status) + return FALSE; - return pixman_region32_contains_rectangle (®ion->rgn, &pbox); + return pixman_region32_contains_point (®ion->rgn, x, y, &box); } +slim_hidden_def (cairo_region_contains_point); diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h index c1d87ae..dc16ea8 100644 --- a/src/cairo-scaled-font-private.h +++ b/src/cairo-scaled-font-private.h @@ -44,6 +44,8 @@ #include "cairo-mutex-type-private.h" #include "cairo-reference-count-private.h" +typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t; + struct _cairo_scaled_font { /* For most cairo objects, the rule for multiple threads is that * the user is responsible for any locking if the same object is @@ -75,7 +77,6 @@ struct _cairo_scaled_font { * scaled_font->mutex in the generic scaled_font code. */ - /* must be first to be stored in a hash table */ cairo_hash_entry_t hash_entry; /* useful bits for _cairo_scaled_font_nil */ @@ -83,26 +84,32 @@ struct _cairo_scaled_font { cairo_reference_count_t ref_count; cairo_user_data_array_t user_data; + cairo_font_face_t *original_font_face; /* may be NULL */ + /* hash key members */ cairo_font_face_t *font_face; /* may be NULL */ cairo_matrix_t font_matrix; /* font space => user space */ cairo_matrix_t ctm; /* user space => device space */ cairo_font_options_t options; - cairo_bool_t placeholder; /* protected by fontmap mutex */ - - cairo_bool_t finished; + unsigned int placeholder : 1; /* protected by fontmap mutex */ + unsigned int holdover : 1; + unsigned int finished : 1; /* "live" scaled_font members */ - cairo_matrix_t scale; /* font space => device space */ - cairo_matrix_t scale_inverse; /* device space => font space */ - double max_scale; /* maximum x/y expansion of scale */ - cairo_font_extents_t extents; /* user space */ + cairo_matrix_t scale; /* font space => device space */ + cairo_matrix_t scale_inverse; /* device space => font space */ + double max_scale; /* maximum x/y expansion of scale */ + cairo_font_extents_t extents; /* user space */ + cairo_font_extents_t fs_extents; /* font space */ /* The mutex protects modification to all subsequent fields. */ cairo_mutex_t mutex; - cairo_cache_t *glyphs; /* glyph index -> cairo_scaled_glyph_t */ + cairo_hash_table_t *glyphs; + cairo_scaled_glyph_page_t *glyph_pages; + cairo_bool_t cache_frozen; + cairo_bool_t global_cache_frozen; /* * One surface backend may store data in each glyph. diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h index 88dedb5..6eb6ce0 100644 --- a/src/cairo-scaled-font-subsets-private.h +++ b/src/cairo-scaled-font-subsets-private.h @@ -332,7 +332,8 @@ cairo_private cairo_int_status_t _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset); typedef struct _cairo_cff_subset { - char *base_font; + char *font_name; + char *ps_name; int *widths; long x_min, y_min, x_max, y_max; long ascent, descent; @@ -404,7 +405,8 @@ cairo_private void _cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset); typedef struct _cairo_truetype_subset { - char *base_font; + char *font_name; + char *ps_name; double *widths; double x_min, y_min, x_max, y_max; double ascent, descent; @@ -635,6 +637,32 @@ _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, unsigned long index, uint32_t *ucs4); +/** + * _cairo_truetype_read_font_name: + * @scaled_font: the #cairo_scaled_font_t + * @ps_name: returns the PostScript name of the font + * or %NULL if the name could not be found. + * @font_name: returns the font name or %NULL if the name could not be found. + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) read the + * PostScript and Font names from a TrueType/OpenType font. + * + * The font name is the full name of the font eg "DejaVu Sans Bold". + * The PostScript name is a shortened name with spaces removed + * suitable for use as the font name in a PS or PDF file eg + * "DejaVuSans-Bold". + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType + * or the name table is not present. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, + char **ps_name, + char **font_name); + #endif /* CAIRO_HAS_FONT_SUBSET */ #endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */ diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c index 066f637..02b32aa 100644 --- a/src/cairo-scaled-font-subsets.c +++ b/src/cairo-scaled-font-subsets.c @@ -163,7 +163,7 @@ _cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, cairo_sub_font_glyph_t *sub_font_glyph; sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t)); - if (sub_font_glyph == NULL) { + if (unlikely (sub_font_glyph == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } @@ -267,7 +267,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, cairo_scaled_font_subsets_glyph_t subset_glyph; sub_font = malloc (sizeof (cairo_sub_font_t)); - if (sub_font == NULL) + if (unlikely (sub_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); sub_font->is_scaled = is_scaled; @@ -284,7 +284,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, sub_font->max_glyphs_per_subset = max_glyphs_per_subset; sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal); - if (sub_font->sub_font_glyphs == NULL) { + if (unlikely (sub_font->sub_font_glyphs == NULL)) { free (sub_font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -294,7 +294,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, * Type 3 fonts */ if (! _cairo_font_face_is_user (scaled_font->font_face)) { status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph); - if (status) { + if (unlikely (status)) { _cairo_hash_table_destroy (sub_font->sub_font_glyphs); free (sub_font); return status; @@ -342,14 +342,14 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, status = _cairo_truetype_index_to_ucs4 (scaled_font, scaled_font_glyph_index, &unicode); - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) { status = scaled_font->backend->index_to_ucs4 (scaled_font, scaled_font_glyph_index, &unicode); - if (status) + if (unlikely (status)) return status; } @@ -360,7 +360,7 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, len = _cairo_ucs4_to_utf8 (unicode, buf); if (len > 0) { sub_font_glyph->utf8 = malloc (len + 1); - if (sub_font_glyph->utf8 == NULL) + if (unlikely (sub_font_glyph->utf8 == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (sub_font_glyph->utf8, buf, len); @@ -372,13 +372,16 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, return CAIRO_STATUS_SUCCESS; } -static cairo_bool_t +static cairo_status_t _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, const char *utf8, - int utf8_len) + int utf8_len, + cairo_bool_t *is_mapped) { + *is_mapped = FALSE; + if (utf8_len < 0) - return FALSE; + return CAIRO_STATUS_SUCCESS; if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0') utf8_len--; @@ -389,28 +392,25 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0) { /* Requested utf8 mapping matches the existing mapping */ - return TRUE; - } - else - { - /* Requested utf8 mapping does not match the existing mapping */ - return FALSE; + *is_mapped = TRUE; } } else { /* No existing mapping. Use the requested mapping */ sub_font_glyph->utf8 = malloc (utf8_len + 1); + if (unlikely (sub_font_glyph->utf8 == NULL)) + return CAIRO_STATUS_NO_MEMORY; + memcpy (sub_font_glyph->utf8, utf8, utf8_len); sub_font_glyph->utf8[utf8_len] = 0; sub_font_glyph->utf8_len = utf8_len; - return TRUE; + *is_mapped = TRUE; } } - /* No mapping was requested. */ - return FALSE; + return CAIRO_STATUS_SUCCESS; } -static cairo_bool_t +static cairo_int_status_t _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, const char *utf8, @@ -418,11 +418,12 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_int_status_t status; _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); - if (_cairo_hash_table_lookup (sub_font->sub_font_glyphs, &key.base, - (cairo_hash_entry_t **) &sub_font_glyph)) - { + sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, + &key.base); + if (sub_font_glyph != NULL) { subset_glyph->font_id = sub_font->font_id; subset_glyph->subset_id = sub_font_glyph->subset_id; subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; @@ -430,13 +431,15 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, subset_glyph->is_composite = sub_font->is_composite; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; - subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len); + status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, + utf8, utf8_len, + &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; - return TRUE; + return status; } - return FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t @@ -450,9 +453,9 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, cairo_status_t status; _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); - if (! _cairo_hash_table_lookup (sub_font->sub_font_glyphs, &key.base, - (cairo_hash_entry_t **) &sub_font_glyph)) - { + sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, + &key.base); + if (sub_font_glyph == NULL) { cairo_scaled_glyph_t *scaled_glyph; if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) @@ -466,7 +469,7 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, * except for Type 3 fonts */ if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) { status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph); - if (status) + if (unlikely (status)) return status; } } @@ -477,7 +480,7 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (status) { + if (unlikely (status)) { _cairo_scaled_font_thaw_cache (sub_font->scaled_font); return status; } @@ -489,19 +492,19 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, scaled_glyph->metrics.y_advance); _cairo_scaled_font_thaw_cache (sub_font->scaled_font); - if (sub_font_glyph == NULL) + if (unlikely (sub_font_glyph == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph, sub_font->scaled_font, scaled_font_glyph_index); - if (status) { + if (unlikely (status)) { _cairo_sub_font_glyph_destroy (sub_font_glyph); return status; } status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); - if (status) { + if (unlikely (status)) { _cairo_sub_font_glyph_destroy (sub_font_glyph); return status; } @@ -524,10 +527,12 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, subset_glyph->is_composite = sub_font->is_composite; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; - subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len); + status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, + utf8, utf8_len, + &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; - return CAIRO_STATUS_SUCCESS; + return status; } static void @@ -542,6 +547,10 @@ _cairo_sub_font_collect (void *entry, void *closure) if (collection->status) return; + collection->status = sub_font->scaled_font->status; + if (collection->status) + return; + for (i = 0; i <= sub_font->current_subset; i++) { collection->subset_id = i; collection->num_glyphs = 0; @@ -596,8 +605,8 @@ _cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type) { cairo_scaled_font_subsets_t *subsets; - subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); - if (subsets == NULL) { + subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); + if (unlikely (subsets == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } @@ -679,28 +688,30 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, if (subsets->type != CAIRO_SUBSETS_SCALED) { key.is_scaled = FALSE; _cairo_sub_font_init_key (&key, scaled_font); - if (_cairo_hash_table_lookup (subsets->unscaled_sub_fonts, &key.base, - (cairo_hash_entry_t **) &sub_font)) - { - if (_cairo_sub_font_lookup_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph)) - return CAIRO_STATUS_SUCCESS; + sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, + &key.base); + if (sub_font != NULL) { + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; } } /* Lookup glyph in scaled subsets */ key.is_scaled = TRUE; _cairo_sub_font_init_key (&key, scaled_font); - if (_cairo_hash_table_lookup (subsets->scaled_sub_fonts, &key.base, - (cairo_hash_entry_t **) &sub_font)) - { - if (_cairo_sub_font_lookup_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph)) - return CAIRO_STATUS_SUCCESS; + sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, + &key.base); + if (sub_font != NULL) { + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; } /* Glyph not found. Determine whether the glyph is outline or @@ -722,7 +733,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, &scaled_glyph); _cairo_scaled_font_thaw_cache (scaled_font); } - if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_status_is_error (status)) return status; if (status == CAIRO_STATUS_SUCCESS && @@ -732,9 +743,9 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, /* Path available. Add to unscaled subset. */ key.is_scaled = FALSE; _cairo_sub_font_init_key (&key, scaled_font); - if (! _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, &key.base, - (cairo_hash_entry_t **) &sub_font)) - { + sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, + &key.base); + if (sub_font == NULL) { font_face = cairo_scaled_font_get_font_face (scaled_font); cairo_matrix_init_identity (&identity); _cairo_font_options_init_default (&font_options); @@ -744,7 +755,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, &identity, &identity, &font_options); - if (unscaled_font->status) + if (unlikely (unscaled_font->status)) return unscaled_font->status; subset_glyph->is_scaled = FALSE; @@ -768,7 +779,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, subset_glyph->is_composite, &sub_font); - if (status) { + if (unlikely (status)) { cairo_scaled_font_destroy (unscaled_font); return status; } @@ -776,7 +787,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts, &sub_font->base); - if (status) { + if (unlikely (status)) { _cairo_sub_font_destroy (sub_font); return status; } @@ -791,9 +802,9 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, /* No path available. Add to scaled subset. */ key.is_scaled = TRUE; _cairo_sub_font_init_key (&key, scaled_font); - if (! _cairo_hash_table_lookup (subsets->scaled_sub_fonts, &key.base, - (cairo_hash_entry_t **) &sub_font)) - { + sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, + &key.base); + if (sub_font == NULL) { subset_glyph->is_scaled = TRUE; subset_glyph->is_composite = FALSE; if (subsets->type == CAIRO_SUBSETS_SCALED) @@ -808,14 +819,14 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, subset_glyph->is_scaled, subset_glyph->is_composite, &sub_font); - if (status) { + if (unlikely (status)) { cairo_scaled_font_destroy (scaled_font); return status; } status = _cairo_hash_table_insert (subsets->scaled_sub_fonts, &sub_font->base); - if (status) { + if (unlikely (status)) { _cairo_sub_font_destroy (sub_font); return status; } @@ -866,7 +877,7 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long)); collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *)); - if (collection.glyphs == NULL || collection.utf8 == NULL) { + if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) { if (collection.glyphs != NULL) free (collection.glyphs); if (collection.utf8 != NULL) @@ -957,7 +968,7 @@ static cairo_status_t create_string_entry (char *s, cairo_string_entry_t **entry) { *entry = malloc (sizeof (cairo_string_entry_t)); - if (*entry == NULL) + if (unlikely (*entry == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_string_init_key (*entry, s); @@ -965,24 +976,31 @@ create_string_entry (char *s, cairo_string_entry_t **entry) return CAIRO_STATUS_SUCCESS; } +static void +_pluck_entry (void *entry, void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + cairo_int_status_t _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset) { unsigned int i; - cairo_status_t status; cairo_hash_table_t *names; cairo_string_entry_t key, *entry; char buf[30]; char *utf8; uint16_t *utf16; int utf16_len; + cairo_status_t status = CAIRO_STATUS_SUCCESS; names = _cairo_hash_table_create (_cairo_string_equal); - if (names == NULL) + if (unlikely (names == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *)); - if (subset->glyph_names == NULL) { + if (unlikely (subset->glyph_names == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_HASH; } @@ -990,17 +1008,17 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset i = 0; if (! _cairo_font_face_is_user (subset->scaled_font->font_face)) { subset->glyph_names[0] = strdup (".notdef"); - if (subset->glyph_names[0] == NULL) { + if (unlikely (subset->glyph_names[0] == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_HASH; } status = create_string_entry (subset->glyph_names[0], &entry); - if (status) + if (unlikely (status)) goto CLEANUP_HASH; status = _cairo_hash_table_insert (names, &entry->base); - if (status) { + if (unlikely (status)) { free (entry); goto CLEANUP_HASH; } @@ -1013,52 +1031,44 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset utf16_len = 0; if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (status) - return status; /* FIXME */ + if (unlikely (status)) + goto CLEANUP_HASH; } if (utf16_len == 1) { - snprintf (buf, sizeof(buf), "uni%04X", (int)(utf16[0])); + snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]); _cairo_string_init_key (&key, buf); - if (_cairo_hash_table_lookup (names, &key.base, - (cairo_hash_entry_t **) &entry)) { - snprintf (buf, sizeof(buf), "g%d", i); - } + entry = _cairo_hash_table_lookup (names, &key.base); + if (entry != NULL) + snprintf (buf, sizeof (buf), "g%d", i); } else { - snprintf (buf, sizeof(buf), "g%d", i); + snprintf (buf, sizeof (buf), "g%d", i); } if (utf16) free (utf16); subset->glyph_names[i] = strdup (buf); - if (subset->glyph_names[i] == NULL) { + if (unlikely (subset->glyph_names[i] == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_HASH; } status = create_string_entry (subset->glyph_names[i], &entry); - if (status) + if (unlikely (status)) goto CLEANUP_HASH; status = _cairo_hash_table_insert (names, &entry->base); - if (status) { + if (unlikely (status)) { free (entry); goto CLEANUP_HASH; } } CLEANUP_HASH: - while (1) { - entry = _cairo_hash_table_random_entry (names, NULL); - if (entry == NULL) - break; - - _cairo_hash_table_remove (names, (cairo_hash_entry_t *) entry); - free (entry); - } + _cairo_hash_table_foreach (names, _pluck_entry, names); _cairo_hash_table_destroy (names); - if (status == CAIRO_STATUS_SUCCESS) + if (likely (status == CAIRO_STATUS_SUCCESS)) return CAIRO_STATUS_SUCCESS; if (subset->glyph_names != NULL) { diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index 3c5a959..e342eb8 100644 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -35,11 +35,47 @@ * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> * Behdad Esfahbod <behdad@behdad.org> + * Chris Wilson <chris@chris-wilson.co.uk> */ +#define _GNU_SOURCE + #include "cairoint.h" #include "cairo-scaled-font-private.h" +#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) +#define ISFINITE(x) isfinite (x) +#else +#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ +#endif + +/* Global Glyph Cache + * + * We maintain a global pool of glyphs split between all active fonts. This + * allows a heavily used individual font to cache more glyphs than we could + * manage if we used per-font glyph caches, but at the same time maintains + * fairness across all fonts and provides a cap on the maximum number of + * global glyphs. + * + * The glyphs are allocated in pages, which are capped in the global pool. + * Using pages means we can reduce the frequency at which we have to probe the + * global pool and ameliorates the memory allocation pressure. + */ + +/* XXX: This number is arbitrary---we've never done any measurement of this. */ +#define MAX_GLYPH_PAGES_CACHED 512 +static cairo_cache_t cairo_scaled_glyph_page_cache; + +#define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 +struct _cairo_scaled_glyph_page { + cairo_cache_entry_t cache_entry; + + struct _cairo_scaled_glyph_page *prev, *next; + + unsigned int num_glyphs; + cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE]; +}; + /* * Notes: * @@ -131,16 +167,16 @@ * Now compare the scaled-glyph space to device-space and surface-space * and convince yourself that: * - * (x_bearing,y_bearing) = (-x,-y) = - device_offset + * (x_bearing,y_bearing) = (-x,-y) = - device_offset * * That's right. If you are not convinced yet, contrast the definition * of the two: * - * "(x_bearing,y_bearing) is the coordinates of top-left of the - * glyph relative to the glyph origin." + * "(x_bearing,y_bearing) is the coordinates of top-left of the + * glyph relative to the glyph origin." * - * "In other words: device_offset is the coordinates of the - * device-space origin relative to the top-left of the surface." + * "In other words: device_offset is the coordinates of the + * device-space origin relative to the top-left of the surface." * * and note that glyph origin = device-space origin. */ @@ -148,46 +184,32 @@ static void _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font); -static cairo_bool_t -_cairo_scaled_glyph_keys_equal (const void *abstract_key_a, const void *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)); -} - static void -_cairo_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph) +_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) { - cairo_scaled_font_t *scaled_font = scaled_glyph->scaled_font; const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend; if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL) surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font); + if (scaled_glyph->surface != NULL) cairo_surface_destroy (&scaled_glyph->surface->base); + if (scaled_glyph->path != NULL) _cairo_path_fixed_destroy (scaled_glyph->path); + if (scaled_glyph->meta_surface != NULL) cairo_surface_destroy (scaled_glyph->meta_surface); } -static void -_cairo_scaled_glyph_destroy (void *abstract_glyph) -{ - cairo_scaled_glyph_t *scaled_glyph = abstract_glyph; - _cairo_scaled_glyph_fini (scaled_glyph); - free (scaled_glyph); -} - #define ZOMBIE 0 static const cairo_scaled_font_t _cairo_scaled_font_nil = { { ZOMBIE }, /* hash_entry */ CAIRO_STATUS_NO_MEMORY, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ + NULL, /* original_font_face */ NULL, /* font_face */ { 1., 0., 0., 1., 0, 0}, /* font_matrix */ { 1., 0., 0., 1., 0, 0}, /* ctm */ @@ -196,16 +218,21 @@ static const cairo_scaled_font_t _cairo_scaled_font_nil = { CAIRO_HINT_STYLE_DEFAULT, CAIRO_HINT_METRICS_DEFAULT} , FALSE, /* placeholder */ + FALSE, /* holdover */ TRUE, /* finished */ { 1., 0., 0., 1., 0, 0}, /* scale */ { 1., 0., 0., 1., 0, 0}, /* scale_inverse */ 1., /* max_scale */ { 0., 0., 0., 0., 0. }, /* extents */ + { 0., 0., 0., 0., 0. }, /* fs_extents */ CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */ NULL, /* glyphs */ + NULL, /* pages */ + FALSE, /* cache_frozen */ + FALSE, /* global_cache_frozen */ NULL, /* surface_backend */ NULL, /* surface_private */ - CAIRO_SCALED_FONT_BACKEND_DEFAULT, + NULL /* backend */ }; /** @@ -247,6 +274,7 @@ _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, * * This function returns the type of the backend used to create * a scaled font. See #cairo_font_type_t for available types. + * However, this function never returns %CAIRO_FONT_TYPE_TOY. * * Return value: The type of @scaled_font. * @@ -308,7 +336,7 @@ typedef struct _cairo_scaled_font_map { int num_holdovers; } cairo_scaled_font_map_t; -static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL; +static cairo_scaled_font_map_t *cairo_scaled_font_map; static int _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b); @@ -320,14 +348,14 @@ _cairo_scaled_font_map_lock (void) if (cairo_scaled_font_map == NULL) { cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); - if (cairo_scaled_font_map == NULL) + if (unlikely (cairo_scaled_font_map == NULL)) goto CLEANUP_MUTEX_LOCK; cairo_scaled_font_map->mru_scaled_font = NULL; cairo_scaled_font_map->hash_table = _cairo_hash_table_create (_cairo_scaled_font_keys_equal); - if (cairo_scaled_font_map->hash_table == NULL) + if (unlikely (cairo_scaled_font_map->hash_table == NULL)) goto CLEANUP_SCALED_FONT_MAP; cairo_scaled_font_map->num_holdovers = 0; @@ -359,7 +387,7 @@ _cairo_scaled_font_map_destroy (void) CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); font_map = cairo_scaled_font_map; - if (font_map == NULL) { + if (unlikely (font_map == NULL)) { goto CLEANUP_MUTEX_LOCK; } @@ -397,7 +425,30 @@ _cairo_scaled_font_map_destroy (void) CLEANUP_MUTEX_LOCK: CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); } +static void +_cairo_scaled_glyph_page_destroy (void *closure) +{ + cairo_scaled_glyph_page_t *page = closure; + cairo_scaled_font_t *scaled_font; + unsigned int n; + + scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + for (n = 0; n < page->num_glyphs; n++) { + _cairo_hash_table_remove (scaled_font->glyphs, + &page->glyphs[n].hash_entry); + _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]); + } + + if (page->prev != NULL) + page->prev->next = page->next; + else + scaled_font->glyph_pages = page->next; + if (page->next != NULL) + page->next->prev = page->prev; + + free (page); +} /* If a scaled font wants to unlock the font map while still being * created (needed for user-fonts), we need to take extra care not @@ -424,11 +475,11 @@ _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex)); status = scaled_font->status; - if (status) + if (unlikely (status)) return status; placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t)); - if (placeholder_scaled_font == NULL) + if (unlikely (placeholder_scaled_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* full initialization is wasteful, but who cares... */ @@ -438,14 +489,14 @@ _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t &scaled_font->ctm, &scaled_font->options, NULL); - if (status) + if (unlikely (status)) goto FREE_PLACEHOLDER; placeholder_scaled_font->placeholder = TRUE; status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table, &placeholder_scaled_font->hash_entry); - if (status) + if (unlikely (status)) goto FINI_PLACEHOLDER; CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); @@ -465,19 +516,18 @@ void _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font) { cairo_scaled_font_t *placeholder_scaled_font; - cairo_bool_t found; CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); - found = _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table, - &scaled_font->hash_entry, - (cairo_hash_entry_t**) &placeholder_scaled_font); - assert (found); + placeholder_scaled_font = + _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table, + &scaled_font->hash_entry); + assert (placeholder_scaled_font != NULL); assert (placeholder_scaled_font->placeholder); assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex)); _cairo_hash_table_remove (cairo_scaled_font_map->hash_table, - &scaled_font->hash_entry); + &placeholder_scaled_font->hash_entry); CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); @@ -516,18 +566,30 @@ _cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t #define FNV1_32_INIT ((uint32_t)0x811c9dc5) static uint32_t -_hash_bytes_fnv (unsigned char *buffer, - int len, - uint32_t hval) +_hash_matrix_fnv (const cairo_matrix_t *matrix, + uint32_t hval) { - while (len--) { + const uint8_t *buffer = (const uint8_t *) matrix; + int len = sizeof (cairo_matrix_t); + do { hval *= FNV_32_PRIME; hval ^= *buffer++; - } + } while (--len); return hval; } +static uint32_t +_hash_mix_bits (uint32_t hash) +{ + hash += hash << 12; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + return hash; +} + static void _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, cairo_font_face_t *font_face, @@ -548,41 +610,65 @@ _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, _cairo_font_options_init_copy (&scaled_font->options, options); /* We do a bytewise hash on the font matrices */ - hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->font_matrix.xx), - sizeof(cairo_matrix_t), hash); - hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->ctm.xx), - sizeof(cairo_matrix_t), hash); + hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash); + hash = _hash_matrix_fnv (&scaled_font->ctm, hash); + hash = _hash_mix_bits (hash); hash ^= (unsigned long) scaled_font->font_face; - hash ^= cairo_font_options_hash (&scaled_font->options); + /* final mixing of bits */ + hash = _hash_mix_bits (hash); + assert (hash != ZOMBIE); scaled_font->hash_entry.hash = hash; } static cairo_bool_t -_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b) +_cairo_scaled_font_keys_equal (const void *abstract_key_a, + const void *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 && + if (key_a->hash_entry.hash != key_b->hash_entry.hash) + return FALSE; + + return key_a->font_face == key_b->font_face && memcmp ((unsigned char *)(&key_a->font_matrix.xx), (unsigned char *)(&key_b->font_matrix.xx), sizeof(cairo_matrix_t)) == 0 && memcmp ((unsigned char *)(&key_a->ctm.xx), (unsigned char *)(&key_b->ctm.xx), sizeof(cairo_matrix_t)) == 0 && - cairo_font_options_equal (&key_a->options, &key_b->options)); + cairo_font_options_equal (&key_a->options, &key_b->options); } -/* XXX: This 256 number is arbitrary---we've never done any measurement - * of this. In fact, having a per-font glyph caches each managed - * separately is probably not what we want anyway. Would probably be - * much better to have a single cache for glyphs with random - * replacement across all glyphs of all fonts. */ -#define MAX_GLYPHS_CACHED_PER_FONT 256 +static cairo_bool_t +_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font, + const cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + return scaled_font->font_face == font_face && + memcmp ((unsigned char *)(&scaled_font->font_matrix.xx), + (unsigned char *)(&font_matrix->xx), + sizeof(cairo_matrix_t)) == 0 && + memcmp ((unsigned char *)(&scaled_font->ctm.xx), + (unsigned char *)(&ctm->xx), + sizeof(cairo_matrix_t)) == 0 && + cairo_font_options_equal (&scaled_font->options, options); +} + +static cairo_bool_t +_cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b) +{ + const cairo_scaled_glyph_t *a = abstract_a; + const cairo_scaled_glyph_t *b = abstract_b; + + return a->hash_entry.hash == b->hash_entry.hash; +} /* * Basic #cairo_scaled_font_t object management @@ -599,7 +685,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_status_t status; status = cairo_font_options_status ((cairo_font_options_t *) options); - if (status) + if (unlikely (status)) return status; _cairo_scaled_font_init_key (scaled_font, font_face, @@ -613,7 +699,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy)); scaled_font->scale_inverse = scaled_font->scale; status = cairo_matrix_invert (&scaled_font->scale_inverse); - if (status) { + if (unlikely (status)) { /* If the font scale matrix is rank 0, just using an all-zero inverse matrix * makes everything work correctly. This make font size 0 work without * producing an error. @@ -633,19 +719,23 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, return status; } - scaled_font->finished = FALSE; - - scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal, - _cairo_scaled_glyph_destroy, - MAX_GLYPHS_CACHED_PER_FONT); - if (scaled_font->glyphs == NULL) + scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal); + if (unlikely (scaled_font->glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + scaled_font->glyph_pages = NULL; + scaled_font->cache_frozen = FALSE; + scaled_font->global_cache_frozen = FALSE; + + scaled_font->holdover = FALSE; + scaled_font->finished = FALSE; + CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1); _cairo_user_data_array_init (&scaled_font->user_data); cairo_font_face_reference (font_face); + scaled_font->original_font_face = NULL; CAIRO_MUTEX_INIT (scaled_font->mutex); @@ -664,25 +754,36 @@ _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font) assert (scaled_font->status == CAIRO_STATUS_SUCCESS); CAIRO_MUTEX_LOCK (scaled_font->mutex); - _cairo_cache_freeze (scaled_font->glyphs); + scaled_font->cache_frozen = TRUE; } void _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) { - _cairo_cache_thaw (scaled_font->glyphs); + scaled_font->cache_frozen = FALSE; + + if (scaled_font->global_cache_frozen) { + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + _cairo_cache_thaw (&cairo_scaled_glyph_page_cache); + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + + scaled_font->global_cache_frozen = FALSE; + } + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } void _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font) { - assert (CAIRO_MUTEX_IS_LOCKED (scaled_font->mutex)); + assert (! scaled_font->cache_frozen); - _cairo_cache_destroy (scaled_font->glyphs); - scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal, - _cairo_scaled_glyph_destroy, - MAX_GLYPHS_CACHED_PER_FONT); + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + while (scaled_font->glyph_pages != NULL) { + _cairo_cache_remove (&cairo_scaled_glyph_page_cache, + &scaled_font->glyph_pages->cache_entry); + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); } cairo_status_t @@ -692,10 +793,12 @@ _cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, cairo_status_t status; double font_scale_x, font_scale_y; + scaled_font->fs_extents = *fs_metrics; + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix, &font_scale_x, &font_scale_y, 1); - if (status) + if (unlikely (status)) return status; /* @@ -717,11 +820,11 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) { scaled_font->finished = TRUE; - if (scaled_font->font_face != NULL) - cairo_font_face_destroy (scaled_font->font_face); + _cairo_scaled_font_reset_cache (scaled_font); + _cairo_hash_table_destroy (scaled_font->glyphs); - if (scaled_font->glyphs != NULL) - _cairo_cache_destroy (scaled_font->glyphs); + cairo_font_face_destroy (scaled_font->font_face); + cairo_font_face_destroy (scaled_font->original_font_face); CAIRO_MUTEX_FINI (scaled_font->mutex); @@ -772,42 +875,49 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, const cairo_font_options_t *options) { cairo_status_t status; - cairo_font_face_t *impl_face; cairo_scaled_font_map_t *font_map; + cairo_font_face_t *original_font_face = font_face; cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL; + double det; + + status = font_face->status; + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + + det = _cairo_matrix_compute_determinant (font_matrix); + if (! ISFINITE (det)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); - if (font_face->status) - return _cairo_scaled_font_create_in_error (font_face->status); + det = _cairo_matrix_compute_determinant (ctm); + if (! ISFINITE (det)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); status = cairo_font_options_status ((cairo_font_options_t *) options); - if (status) + if (unlikely (status)) return _cairo_scaled_font_create_in_error (status); /* Note that degenerate ctm or font_matrix *are* allowed. * We want to support a font size of 0. */ if (font_face->backend->get_implementation != NULL) { - /* indirect implementation, lookup the face that is used for the key */ - status = font_face->backend->get_implementation (font_face, &impl_face); - if (status) - return _cairo_scaled_font_create_in_error (status); - } else - impl_face = font_face; + font_face = font_face->backend->get_implementation (font_face, + font_matrix, + ctm, + options); + } font_map = _cairo_scaled_font_map_lock (); - if (font_map == NULL) + if (unlikely (font_map == NULL)) return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - _cairo_scaled_font_init_key (&key, impl_face, - font_matrix, ctm, options); scaled_font = font_map->mru_scaled_font; if (scaled_font != NULL && - scaled_font->hash_entry.hash == key.hash_entry.hash && - _cairo_scaled_font_keys_equal (scaled_font, &key)) + _cairo_scaled_font_matches (scaled_font, + font_face, font_matrix, ctm, options)) { assert (! scaled_font->placeholder); - if (scaled_font->status == CAIRO_STATUS_SUCCESS) { + if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather * than calling into cairo_scaled_font_reference), since we * must modify the reference count while our lock is still @@ -818,13 +928,17 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, } /* the font has been put into an error status - abandon the cache */ - _cairo_hash_table_remove (font_map->hash_table, &key.hash_entry); + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); scaled_font->hash_entry.hash = ZOMBIE; } else { - while (_cairo_hash_table_lookup (font_map->hash_table, &key.hash_entry, - (cairo_hash_entry_t**) &scaled_font)) + _cairo_scaled_font_init_key (&key, font_face, + font_matrix, ctm, options); + + while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, + &key.hash_entry))) { if (! scaled_font->placeholder) break; @@ -839,25 +953,30 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, /* If the original reference count is 0, then this font must have * been found in font_map->holdovers, (which means this caching is * actually working). So now we remove it from the holdovers - * array. */ + * array, unless we caught the font in the middle of destruction. + */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { - int i; - - for (i = 0; i < font_map->num_holdovers; i++) - if (font_map->holdovers[i] == scaled_font) - break; - assert (i < font_map->num_holdovers); + if (scaled_font->holdover) { + int i; + + for (i = 0; i < font_map->num_holdovers; i++) { + if (font_map->holdovers[i] == scaled_font) { + font_map->num_holdovers--; + memmove (&font_map->holdovers[i], + &font_map->holdovers[i+1], + (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); + break; + } + } - font_map->num_holdovers--; - memmove (&font_map->holdovers[i], - &font_map->holdovers[i+1], - (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); + scaled_font->holdover = FALSE; + } /* reset any error status */ scaled_font->status = CAIRO_STATUS_SUCCESS; } - if (scaled_font->status == CAIRO_STATUS_SUCCESS) { + if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather * than calling into cairo_scaled_font_reference), since we * must modify the reference count while our lock is still @@ -877,7 +996,8 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, } /* the font has been put into an error status - abandon the cache */ - _cairo_hash_table_remove (font_map->hash_table, &key.hash_entry); + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); scaled_font->hash_entry.hash = ZOMBIE; } } @@ -885,15 +1005,31 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, /* Otherwise create it and insert it into the hash table. */ status = font_face->backend->scaled_font_create (font_face, font_matrix, ctm, options, &scaled_font); - if (status) { + /* Did we leave the backend in an error state? */ + if (unlikely (status)) { _cairo_scaled_font_map_unlock (); status = _cairo_font_face_set_error (font_face, status); return _cairo_scaled_font_create_in_error (status); } + /* Or did we encounter an error whilst constructing the scaled font? */ + if (unlikely (scaled_font->status)) { + _cairo_scaled_font_map_unlock (); + return scaled_font; + } + /* Our caching above is defeated if the backend switches fonts on us - + * e.g. old incarnations of toy-font-face and lazily resolved + * ft-font-faces + */ + assert (scaled_font->font_face == font_face); + + scaled_font->original_font_face = + cairo_font_face_reference (original_font_face); + + assert (scaled_font->hash_entry.hash == key.hash_entry.hash); status = _cairo_hash_table_insert (font_map->hash_table, &scaled_font->hash_entry); - if (status == CAIRO_STATUS_SUCCESS) { + if (likely (status == CAIRO_STATUS_SUCCESS)) { old = font_map->mru_scaled_font; font_map->mru_scaled_font = scaled_font; _cairo_reference_count_inc (&scaled_font->ref_count); @@ -901,7 +1037,7 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, _cairo_scaled_font_map_unlock (); - if (status) { + if (unlikely (status)) { /* We can't call _cairo_scaled_font_destroy here since it expects * that the font has already been successfully inserted into the * hash table. */ @@ -912,6 +1048,9 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + return scaled_font; } slim_hidden_def (cairo_scaled_font_create); @@ -931,9 +1070,9 @@ _cairo_scaled_font_create_in_error (cairo_status_t status) CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); scaled_font = _cairo_scaled_font_nil_objects[status]; - if (scaled_font == NULL) { + if (unlikely (scaled_font == NULL)) { scaled_font = malloc (sizeof (cairo_scaled_font_t)); - if (scaled_font == NULL) { + if (unlikely (scaled_font == NULL)) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; @@ -964,6 +1103,13 @@ _cairo_scaled_font_reset_static_data (void) } } CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + if (cairo_scaled_glyph_page_cache.hash_table != NULL) { + _cairo_cache_fini (&cairo_scaled_glyph_page_cache); + cairo_scaled_glyph_page_cache.hash_table = NULL; + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); } /** @@ -1017,13 +1163,21 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count)) + return; + font_map = _cairo_scaled_font_map_lock (); assert (font_map != NULL); - if (_cairo_reference_count_dec_and_test (&scaled_font->ref_count)) { - + /* Another thread may have resurrected the font whilst we waited */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { + if (! scaled_font->placeholder && + scaled_font->hash_entry.hash != ZOMBIE) + { + /* Another thread may have already inserted us into the holdovers */ + if (scaled_font->holdover) + goto unlock; - if (!scaled_font->placeholder && scaled_font->hash_entry.hash != ZOMBIE) { /* Rather than immediately destroying this object, we put it into * the font_map->holdovers array in case it will get used again * soon (and is why we must hold the lock over the atomic op on @@ -1031,12 +1185,12 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) * destroy the least-recently-used holdover. */ - if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) - { + if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { lru = font_map->holdovers[0]; assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count)); - _cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry); + _cairo_hash_table_remove (font_map->hash_table, + &lru->hash_entry); font_map->num_holdovers--; memmove (&font_map->holdovers[0], @@ -1044,13 +1198,13 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); } - font_map->holdovers[font_map->num_holdovers] = scaled_font; - font_map->num_holdovers++; + font_map->holdovers[font_map->num_holdovers++] = scaled_font; + scaled_font->holdover = TRUE; } else lru = scaled_font; - } + unlock: _cairo_scaled_font_map_unlock (); /* If we pulled an item from the holdovers array, (while the font @@ -1059,7 +1213,7 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) * safely call fini on it without any lock held. This is desirable * as we never want to call into any backend function with a lock * held. */ - if (lru) { + if (lru != NULL) { _cairo_scaled_font_fini_internal (lru); free (lru); } @@ -1108,6 +1262,7 @@ cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, return _cairo_user_data_array_get_data (&scaled_font->user_data, key); } +slim_hidden_def (cairo_scaled_font_get_user_data); /** * cairo_scaled_font_set_user_data: @@ -1139,13 +1294,7 @@ cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, return _cairo_user_data_array_set_data (&scaled_font->user_data, key, user_data, destroy); } - -static cairo_bool_t -_cairo_scaled_font_is_frozen (cairo_scaled_font_t *scaled_font) -{ - return CAIRO_MUTEX_IS_LOCKED (scaled_font->mutex) && - scaled_font->glyphs->freeze_count > 0; -} +slim_hidden_def (cairo_scaled_font_set_user_data); /* Public font API follows. */ @@ -1216,7 +1365,7 @@ cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, &glyphs, &num_glyphs, NULL, NULL, NULL); - if (status) { + if (unlikely (status)) { status = _cairo_scaled_font_set_error (scaled_font, status); goto ZERO_EXTENTS; } @@ -1299,7 +1448,7 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); - if (status) { + if (unlikely (status)) { status = _cairo_scaled_font_set_error (scaled_font, status); goto UNLOCK; } @@ -1514,9 +1663,15 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, cairo_status_t status; cairo_glyph_t *orig_glyphs; cairo_text_cluster_t *orig_clusters; + struct glyph_lut_elt { + uint32_t unicode; + unsigned long index; + double x_advance; + double y_advance; + } glyph_lut[256]; status = scaled_font->status; - if (status) + if (unlikely (status)) return status; /* A slew of sanity checks */ @@ -1524,7 +1679,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, /* glyphs and num_glyphs can't be NULL */ if (glyphs == NULL || num_glyphs == NULL) { - status = CAIRO_STATUS_NULL_POINTER; + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto BAIL; } @@ -1536,7 +1691,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, if ((utf8_len && utf8 == NULL) || (clusters && num_clusters == NULL) || (clusters && cluster_flags == NULL)) { - status = CAIRO_STATUS_NULL_POINTER; + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto BAIL; } @@ -1568,7 +1723,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, if (utf8_len < 0 || *num_glyphs < 0 || (num_clusters && *num_clusters < 0)) { - status = CAIRO_STATUS_NEGATIVE_COUNT; + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto BAIL; } @@ -1579,7 +1734,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, /* validate input so backend does not have to */ status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars); - if (status) + if (unlikely (status)) goto BAIL; _cairo_scaled_font_freeze_cache (scaled_font); @@ -1588,46 +1743,42 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, orig_clusters = clusters ? *clusters : NULL; if (scaled_font->backend->text_to_glyphs) { - status = scaled_font->backend->text_to_glyphs (scaled_font, x, y, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - if (status == CAIRO_STATUS_SUCCESS) { - /* The checks here are crude; we only should do them in * user-font backend, but they don't hurt here. This stuff * can be hard to get right. */ if (*num_glyphs < 0) { - status = CAIRO_STATUS_NEGATIVE_COUNT; + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE; } if (num_glyphs && *glyphs == NULL) { - status = CAIRO_STATUS_NULL_POINTER; + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE; } if (clusters) { - if (*num_clusters < 0) { - status = CAIRO_STATUS_NEGATIVE_COUNT; + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE; } if (num_clusters && *clusters == NULL) { - status = CAIRO_STATUS_NULL_POINTER; + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE; } - /* Dont trust the backend, validate clusters! */ - status = _cairo_validate_text_clusters (utf8, utf8_len, - *glyphs, *num_glyphs, - *clusters, *num_clusters, - *cluster_flags); + /* Don't trust the backend, validate clusters! */ + status = + _cairo_validate_text_clusters (utf8, utf8_len, + *glyphs, *num_glyphs, + *clusters, *num_clusters, + *cluster_flags); } } @@ -1637,7 +1788,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, if (*num_glyphs < num_chars) { *glyphs = cairo_glyph_allocate (num_chars); - if (*glyphs == NULL) { + if (unlikely (*glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto DONE; } @@ -1647,7 +1798,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, if (clusters) { if (*num_clusters < num_chars) { *clusters = cairo_text_cluster_allocate (num_chars); - if (*clusters == NULL) { + if (unlikely (*clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto DONE; } @@ -1655,40 +1806,57 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, *num_clusters = num_chars; } + for (i = 0; i < ARRAY_LENGTH (glyph_lut); i++) + glyph_lut[i].unicode = ~0U; + p = utf8; for (i = 0; i < num_chars; i++) { int num_bytes; uint32_t unicode; cairo_scaled_glyph_t *scaled_glyph; + struct glyph_lut_elt *glyph_slot; num_bytes = _cairo_utf8_get_char_validated (p, &unicode); p += num_bytes; - (*glyphs)[i].index = (*scaled_font->backend->ucs4_to_index) (scaled_font, unicode); (*glyphs)[i].x = x; (*glyphs)[i].y = y; + glyph_slot = &glyph_lut[unicode % ARRAY_LENGTH (glyph_lut)]; + if (glyph_slot->unicode == unicode) { + (*glyphs)[i].index = glyph_slot->index; + x += glyph_slot->x_advance; + y += glyph_slot->y_advance; + } else { + (*glyphs)[i].index = + (*scaled_font->backend->ucs4_to_index) (scaled_font, unicode); + + status = _cairo_scaled_glyph_lookup (scaled_font, + (*glyphs)[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + goto DONE; + + x += scaled_glyph->metrics.x_advance; + y += scaled_glyph->metrics.y_advance; + + glyph_slot->unicode = unicode; + glyph_slot->index = (*glyphs)[i].index; + glyph_slot->x_advance = scaled_glyph->metrics.x_advance; + glyph_slot->y_advance = scaled_glyph->metrics.y_advance; + } + if (clusters) { (*clusters)[i].num_bytes = num_bytes; (*clusters)[i].num_glyphs = 1; } - - status = _cairo_scaled_glyph_lookup (scaled_font, - (*glyphs)[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (status) { - goto DONE; - } - - x += scaled_glyph->metrics.x_advance; - y += scaled_glyph->metrics.y_advance; } DONE: /* error that should be logged on scaled_font happened */ _cairo_scaled_font_thaw_cache (scaled_font); - if (status) { + if (unlikely (status)) { *num_glyphs = 0; if (*glyphs != orig_glyphs) { cairo_glyph_free (*glyphs); @@ -1731,42 +1899,53 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, int i; cairo_point_int_t min = { CAIRO_RECT_INT_MAX, CAIRO_RECT_INT_MAX }; cairo_point_int_t max = { CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN }; + cairo_scaled_glyph_t *glyph_cache[64]; - if (scaled_font->status) + if (unlikely (scaled_font->status)) return scaled_font->status; _cairo_scaled_font_freeze_cache (scaled_font); + memset (glyph_cache, 0, sizeof (glyph_cache)); + for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; int left, top; int right, bottom; int x, y; + int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (status) - break; + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) + { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + glyph_cache[cache_index] = scaled_glyph; + } /* XXX glyph images are snapped to pixel locations */ x = _cairo_lround (glyphs[i].x); y = _cairo_lround (glyphs[i].y); - left = x + _cairo_fixed_integer_floor(scaled_glyph->bbox.p1.x); + left = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); top = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); - right = x + _cairo_fixed_integer_ceil(scaled_glyph->bbox.p2.x); + right = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); bottom = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); - if (left < min.x) min.x = left; - if (right > max.x) max.x = right; - if (top < min.y) min.y = top; + if (left < min.x) min.x = left; + if (right > max.x) max.x = right; + if (top < min.y) min.y = top; if (bottom > max.y) max.y = bottom; } _cairo_scaled_font_thaw_cache (scaled_font); - if (status) + if (unlikely (status)) return _cairo_scaled_font_set_error (scaled_font, status); if (min.x < max.x && min.y < max.y) { @@ -1785,7 +1964,7 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, @@ -1848,7 +2027,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); - if (status) + if (unlikely (status)) goto CLEANUP_MASK; glyph_surface = scaled_glyph->surface; @@ -1859,10 +2038,9 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, mask_format = glyph_surface->format; mask = cairo_image_surface_create (mask_format, width, height); - if (mask->status) { - status = mask->status; + status = mask->status; + if (unlikely (status)) goto CLEANUP_MASK; - } } /* If we have glyphs of different formats, we "upgrade" the mask @@ -1872,7 +2050,6 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, _cairo_format_bits_per_pixel (glyph_surface->format) ) { cairo_surface_t *new_mask; - cairo_surface_pattern_t mask_pattern; switch (glyph_surface->format) { case CAIRO_FORMAT_ARGB32: @@ -1908,7 +2085,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, _cairo_pattern_fini (&mask_pattern.base); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (new_mask); goto CLEANUP_MASK; } @@ -1936,7 +2113,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, _cairo_pattern_fini (&glyph_pattern.base); - if (status) + if (unlikely (status)) goto CLEANUP_MASK; } @@ -1970,7 +2147,8 @@ typedef struct _cairo_scaled_glyph_path_closure { } cairo_scaled_glyph_path_closure_t; static cairo_status_t -_scaled_glyph_path_move_to (void *abstract_closure, cairo_point_t *point) +_scaled_glyph_path_move_to (void *abstract_closure, + const cairo_point_t *point) { cairo_scaled_glyph_path_closure_t *closure = abstract_closure; @@ -1980,7 +2158,8 @@ _scaled_glyph_path_move_to (void *abstract_closure, cairo_point_t *point) } static cairo_status_t -_scaled_glyph_path_line_to (void *abstract_closure, cairo_point_t *point) +_scaled_glyph_path_line_to (void *abstract_closure, + const cairo_point_t *point) { cairo_scaled_glyph_path_closure_t *closure = abstract_closure; @@ -1991,9 +2170,9 @@ _scaled_glyph_path_line_to (void *abstract_closure, cairo_point_t *point) static cairo_status_t _scaled_glyph_path_curve_to (void *abstract_closure, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2) + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) { cairo_scaled_glyph_path_closure_t *closure = abstract_closure; @@ -2023,29 +2202,29 @@ _add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y) status = _cairo_path_fixed_move_to (path, _cairo_fixed_from_int (x), _cairo_fixed_from_int (y)); - if (status) + if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (1), _cairo_fixed_from_int (0)); - if (status) + if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (0), _cairo_fixed_from_int (1)); - if (status) + if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (-1), _cairo_fixed_from_int (0)); - if (status) + if (unlikely (status)) return status; status = _cairo_path_fixed_close_path (path); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -2072,35 +2251,37 @@ static cairo_status_t _trace_mask_to_path (cairo_image_surface_t *mask, cairo_path_fixed_t *path) { - cairo_status_t status; - cairo_image_surface_t *a1_mask; - uint8_t *row, *byte_ptr, byte; + const uint8_t *row; int rows, cols, bytes_per_row; int x, y, bit; double xoff, yoff; + cairo_status_t status; - if (mask->format == CAIRO_FORMAT_A1) - a1_mask = (cairo_image_surface_t *) cairo_surface_reference (&mask->base); - else - a1_mask = _cairo_image_surface_clone (mask, CAIRO_FORMAT_A1); - - status = cairo_surface_status (&a1_mask->base); - if (status) { - cairo_surface_destroy (&a1_mask->base); + mask = _cairo_image_surface_coerce (mask, CAIRO_FORMAT_A1); + status = mask->base.status; + if (unlikely (status)) return status; - } cairo_surface_get_device_offset (&mask->base, &xoff, &yoff); - bytes_per_row = (a1_mask->width + 7) / 8; - for (y = 0, row = a1_mask->data, rows = a1_mask->height; rows; row += a1_mask->stride, rows--, y++) { - for (x = 0, byte_ptr = row, cols = bytes_per_row; cols; byte_ptr++, cols--) { - byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte_ptr); - for (bit = 7; bit >= 0 && x < a1_mask->width; bit--, x++) { - if (byte & (1 << bit)) { + bytes_per_row = (mask->width + 7) / 8; + row = mask->data; + for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) { + const uint8_t *byte_ptr = row; + x = 0; + for (cols = bytes_per_row; cols--; ) { + uint8_t byte = *byte_ptr++; + if (byte == 0) { + x += 8; + continue; + } + + byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte); + for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) { + if (byte & bit) { status = _add_unit_rectangle_to_path (path, x - xoff, y - yoff); - if (status) + if (unlikely (status)) goto BAIL; } } @@ -2108,7 +2289,7 @@ _trace_mask_to_path (cairo_image_surface_t *mask, } BAIL: - cairo_surface_destroy (&a1_mask->base); + cairo_surface_destroy (&mask->base); return status; } @@ -2125,7 +2306,7 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, cairo_path_fixed_t *glyph_path; status = scaled_font->status; - if (status) + if (unlikely (status)) return status; closure.path = path; @@ -2149,17 +2330,17 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); - if (status) + if (unlikely (status)) goto BAIL; glyph_path = _cairo_path_fixed_create (); - if (glyph_path == NULL) { + if (unlikely (glyph_path == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } status = _trace_mask_to_path (scaled_glyph->surface, glyph_path); - if (status) { + if (unlikely (status)) { _cairo_path_fixed_destroy (glyph_path); goto BAIL; } @@ -2178,7 +2359,7 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, if (glyph_path != scaled_glyph->path) _cairo_path_fixed_destroy (glyph_path); - if (status) + if (unlikely (status)) goto BAIL; } BAIL: @@ -2209,6 +2390,8 @@ _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; double device_x_advance, device_y_advance; + scaled_glyph->fs_metrics = *fs_metrics; + for (hm = 0.0; hm <= 1.0; hm += 1.0) for (wm = 0.0; wm <= 1.0; wm += 1.0) { double x, y; @@ -2278,6 +2461,9 @@ _cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, { if (scaled_glyph->surface != NULL) cairo_surface_destroy (&scaled_glyph->surface->base); + + /* sanity check the backend glyph contents */ + _cairo_debug_check_image_surface_is_defined (&surface->base); scaled_glyph->surface = surface; } @@ -2301,6 +2487,93 @@ _cairo_scaled_glyph_set_meta_surface (cairo_scaled_glyph_t *scaled_glyph, scaled_glyph->meta_surface = meta_surface; } +static cairo_bool_t +_cairo_scaled_glyph_page_can_remove (const void *closure) +{ + const cairo_scaled_glyph_page_t *page = closure; + const cairo_scaled_font_t *scaled_font; + + scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + return scaled_font->cache_frozen == 0; +} + +static cairo_status_t +_cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t **scaled_glyph) +{ + cairo_scaled_glyph_page_t *page; + cairo_status_t status; + + /* only the first page in the list may contain available slots */ + page = scaled_font->glyph_pages; + if (page != NULL && page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) { + *scaled_glyph = &page->glyphs[page->num_glyphs++]; + return CAIRO_STATUS_SUCCESS; + } + + page = malloc (sizeof (cairo_scaled_glyph_page_t)); + if (unlikely (page == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + page->cache_entry.hash = (unsigned long) scaled_font; + page->cache_entry.size = 1; /* XXX occupancy weighting? */ + page->num_glyphs = 0; + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + if (scaled_font->global_cache_frozen == FALSE) { + if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) { + status = _cairo_cache_init (&cairo_scaled_glyph_page_cache, + NULL, + _cairo_scaled_glyph_page_can_remove, + _cairo_scaled_glyph_page_destroy, + MAX_GLYPH_PAGES_CACHED); + if (unlikely (status)) { + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + free (page); + return status; + } + } + + _cairo_cache_freeze (&cairo_scaled_glyph_page_cache); + scaled_font->global_cache_frozen = TRUE; + } + + status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache, + &page->cache_entry); + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + if (unlikely (status)) { + free (page); + return status; + } + + page->next = scaled_font->glyph_pages; + page->prev = NULL; + if (scaled_font->glyph_pages != NULL) + scaled_font->glyph_pages->prev = page; + scaled_font->glyph_pages = page; + + *scaled_glyph = &page->glyphs[page->num_glyphs++]; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_scaled_glyph_page_t *page; + + page = scaled_font->glyph_pages; + assert (page != NULL && scaled_glyph == &page->glyphs[page->num_glyphs-1]); + + _cairo_scaled_glyph_fini (scaled_font, scaled_glyph); + + if (--page->num_glyphs == 0) { + _cairo_cache_remove (&cairo_scaled_glyph_page_cache, + &page->cache_entry); + assert (scaled_font->glyph_pages != page); + } +} + /** * _cairo_scaled_glyph_lookup: * @scaled_font: a #cairo_scaled_font_t @@ -2334,78 +2607,75 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_info_t info, cairo_scaled_glyph_t **scaled_glyph_ret) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_cache_entry_t key; + cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; - cairo_scaled_glyph_info_t need_info; + cairo_scaled_glyph_info_t need_info; - if (scaled_font->status) + if (unlikely (scaled_font->status)) return scaled_font->status; - assert (_cairo_scaled_font_is_frozen (scaled_font)); + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - key.hash = index; /* * Check cache for glyph */ - info |= CAIRO_SCALED_GLYPH_INFO_METRICS; - if (!_cairo_cache_lookup (scaled_font->glyphs, &key, - (cairo_cache_entry_t **) &scaled_glyph)) - { - /* - * On miss, create glyph and insert into cache - */ - scaled_glyph = malloc (sizeof (cairo_scaled_glyph_t)); - if (scaled_glyph == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs, + (cairo_hash_entry_t *) &index); + if (scaled_glyph == NULL) { + status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph); + if (unlikely (status)) goto CLEANUP; - } - _cairo_scaled_glyph_set_index(scaled_glyph, index); - scaled_glyph->cache_entry.size = 1; /* We currently don't differentiate on glyph size at all */ - scaled_glyph->scaled_font = scaled_font; - scaled_glyph->surface = NULL; - scaled_glyph->path = NULL; - scaled_glyph->meta_surface = NULL; - scaled_glyph->surface_private = NULL; + memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t)); + _cairo_scaled_glyph_set_index (scaled_glyph, index); /* ask backend to initialize metrics and shape fields */ - status = (*scaled_font->backend-> - scaled_glyph_init) (scaled_font, scaled_glyph, info); - if (status) { - _cairo_scaled_glyph_destroy (scaled_glyph); + status = + scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + info | CAIRO_SCALED_GLYPH_INFO_METRICS); + if (unlikely (status)) { + _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); goto CLEANUP; } - /* on success, the cache takes ownership of the scaled_glyph */ - status = _cairo_cache_insert (scaled_font->glyphs, - &scaled_glyph->cache_entry); - if (status) { - _cairo_scaled_glyph_destroy (scaled_glyph); + status = _cairo_hash_table_insert (scaled_font->glyphs, + &scaled_glyph->hash_entry); + if (unlikely (status)) { + _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); goto CLEANUP; } } + /* * Check and see if the glyph, as provided, - * already has the requested data and ammend it if not + * already has the requested data and amend it if not */ need_info = 0; if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 && scaled_glyph->surface == NULL) + { need_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE; + } - if (((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && - scaled_glyph->path == NULL)) + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && + scaled_glyph->path == NULL) + { need_info |= CAIRO_SCALED_GLYPH_INFO_PATH; + } - if (((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 && - scaled_glyph->meta_surface == NULL)) + if ((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 && + scaled_glyph->meta_surface == NULL) + { need_info |= CAIRO_SCALED_GLYPH_INFO_META_SURFACE; + } if (need_info) { - status = (*scaled_font->backend-> - scaled_glyph_init) (scaled_font, scaled_glyph, need_info); - if (status) + status = scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + need_info); + if (unlikely (status)) goto CLEANUP; /* Don't trust the scaled_glyph_init() return value, the font @@ -2414,26 +2684,29 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, * glyph info. */ if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 && - scaled_glyph->surface == NULL) { + scaled_glyph->surface == NULL) + { status = CAIRO_INT_STATUS_UNSUPPORTED; goto CLEANUP; } if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && - scaled_glyph->path == NULL) { + scaled_glyph->path == NULL) + { status = CAIRO_INT_STATUS_UNSUPPORTED; goto CLEANUP; } if ((info & CAIRO_SCALED_GLYPH_INFO_META_SURFACE) != 0 && - scaled_glyph->meta_surface == NULL) { + scaled_glyph->meta_surface == NULL) + { status = CAIRO_INT_STATUS_UNSUPPORTED; goto CLEANUP; } } CLEANUP: - if (status) { + if (unlikely (status)) { /* It's not an error for the backend to not support the info we want. */ if (status != CAIRO_INT_STATUS_UNSUPPORTED) status = _cairo_scaled_font_set_error (scaled_font, status); @@ -2456,7 +2729,9 @@ _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) * cairo_scaled_font_get_font_face: * @scaled_font: a #cairo_scaled_font_t * - * Gets the font face that this scaled font was created for. + * Gets the font face that this scaled font uses. This is the + * font face passed to cairo_scaled_font_create() if that font face + * was not of type %CAIRO_FONT_TYPE_TOY. * * Return value: The #cairo_font_face_t with which @scaled_font was * created. @@ -2469,6 +2744,9 @@ cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font) if (scaled_font->status) return (cairo_font_face_t*) &_cairo_font_face_nil; + if (scaled_font->original_font_face != NULL) + return scaled_font->original_font_face; + return scaled_font->font_face; } slim_hidden_def (cairo_scaled_font_get_font_face); @@ -2502,6 +2780,9 @@ slim_hidden_def (cairo_scaled_font_get_font_matrix); * @ctm: return value for the CTM * * Stores the CTM with which @scaled_font was created into @ctm. + * Note that the translation offsets (x0, y0) of the CTM are ignored + * by cairo_scaled_font_create(). So, the matrix this + * function returns always has 0,0 as x0,y0. * * Since: 1.2 **/ diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c new file mode 100644 index 0000000..2585a50 --- /dev/null +++ b/src/cairo-script-surface.c @@ -0,0 +1,2603 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * 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 Chris Wilson. + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +/* The script surface is one that records all operations performed on + * it in the form of a procedural script, similar in fashion to + * PostScript but using Cairo's imaging model. In essence, this is + * equivalent to the meta-surface, but as there is no impedance mismatch + * between Cairo and CairoScript, we can generate output immediately + * without having to copy and hold the data in memory. + */ + +#include "cairoint.h" + +#include "cairo-script.h" + +#include "cairo-analysis-surface-private.h" +#include "cairo-ft-private.h" +#include "cairo-meta-surface-private.h" +#include "cairo-output-stream-private.h" + +#define _cairo_output_stream_puts(S, STR) \ + _cairo_output_stream_write ((S), (STR), strlen (STR)) + +#define static cairo_warn static + +typedef struct _cairo_script_vmcontext cairo_script_vmcontext_t; +typedef struct _cairo_script_surface cairo_script_surface_t; +typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; +typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; + +struct _cairo_script_vmcontext { + int ref; + + cairo_output_stream_t *stream; + cairo_script_mode_t mode; + + struct _bitmap { + unsigned long min; + unsigned long count; + unsigned int map[64]; + struct _bitmap *next; + } surface_id, font_id; + + cairo_script_surface_t *current_target; + + cairo_script_surface_font_private_t *fonts; +}; + +struct _cairo_script_surface_font_private { + cairo_script_vmcontext_t *ctx; + cairo_bool_t has_sfnt; + unsigned long id; + unsigned long subset_glyph_index; + cairo_script_surface_font_private_t *prev, *next; + cairo_scaled_font_t *parent; +}; + +struct _cairo_script_implicit_context { + cairo_operator_t current_operator; + cairo_fill_rule_t current_fill_rule; + double current_tolerance; + cairo_antialias_t current_antialias; + cairo_stroke_style_t current_style; + cairo_pattern_t *current_source; + cairo_matrix_t current_ctm; + cairo_matrix_t current_font_matrix; + cairo_font_options_t current_font_options; + cairo_scaled_font_t *current_scaled_font; + cairo_path_fixed_t current_path; +}; + +struct _cairo_script_surface { + cairo_surface_t base; + + cairo_script_vmcontext_t *ctx; + + unsigned long id; + + double width, height; + + /* implicit flattened context */ + cairo_script_implicit_context_t cr; +}; + +static const cairo_surface_backend_t _cairo_script_surface_backend; + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx, + double width, + double height); + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); + +static void +_bitmap_release_id (struct _bitmap *b, unsigned long token) +{ + struct _bitmap **prev = NULL; + + do { + if (token < b->min + sizeof (b->map) * CHAR_BIT) { + unsigned int bit, elem; + + token -= b->min; + elem = token / (sizeof (b->map[0]) * CHAR_BIT); + bit = token % (sizeof (b->map[0]) * CHAR_BIT); + b->map[elem] &= ~(1 << bit); + if (! --b->count && prev) { + *prev = b->next; + free (b); + } + return; + } + prev = &b->next; + b = b->next; + } while (b != NULL); +} + +static cairo_status_t +_bitmap_next_id (struct _bitmap *b, + unsigned long *id) +{ + struct _bitmap *bb, **prev = NULL; + unsigned long min = 0; + + do { + if (b->min != min) + break; + + if (b->count < sizeof (b->map) * CHAR_BIT) { + unsigned int n, m, bit; + for (n = 0; n < ARRAY_LENGTH (b->map); n++) { + if (b->map[n] == (unsigned int) -1) + continue; + + for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) { + if ((b->map[n] & bit) == 0) { + b->map[n] |= bit; + b->count++; + *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; + return CAIRO_STATUS_SUCCESS; + } + } + } + } + min += sizeof (b->map) * CHAR_BIT; + + prev = &b->next; + b = b->next; + } while (b != NULL); + + bb = malloc (sizeof (struct _bitmap)); + if (unlikely (bb == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *prev = bb; + bb->next = b; + bb->min = min; + bb->count = 1; + bb->map[0] = 0x1; + memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); + *id = min; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_direction_to_string (cairo_bool_t backward) +{ + static const char *names[] = { + "FORWARD", + "BACKWARD" + }; + assert (backward < ARRAY_LENGTH (names)); + return names[backward]; +} + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE" /* CAIRO_OPERATOR_SATURATE */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static cairo_bool_t +_cairo_script_surface_owns_context (cairo_script_surface_t *surface) +{ + return surface->ctx->current_target == surface; +} + +static cairo_status_t +_emit_context (cairo_script_surface_t *surface) +{ + if (_cairo_script_surface_owns_context (surface)) + return CAIRO_STATUS_SUCCESS; + + if (surface->ctx->current_target != NULL) + _cairo_output_stream_puts (surface->ctx->stream, "pop\n"); + + surface->ctx->current_target = surface; + + if (surface->id == (unsigned long) -1) { + cairo_status_t status; + + status = _bitmap_next_id (&surface->ctx->surface_id, + &surface->id); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /width %f set\n" + " /height %f set\n", + surface->width, + surface->height); + if (surface->base.x_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || + surface->base.y_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /fallback-resolution [%f %f] set\n", + surface->base.x_fallback_resolution, + surface->base.y_fallback_resolution); + } + if (surface->base.device_transform.x0 != 0. || + surface->base.device_transform.y0 != 0.) + { + /* XXX device offset is encoded into the pattern matrices etc. */ + _cairo_output_stream_printf (surface->ctx->stream, + " %%/device-offset [%f %f] set\n", + surface->base.device_transform.x0, + surface->base.device_transform.y0); + } + _cairo_output_stream_printf (surface->ctx->stream, + " surface dup /s%lu exch def\n" + "context dup /c%lu exch def\n", + surface->id, + surface->id); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "c%lu\n", + surface->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_operator (cairo_script_surface_t *surface, + cairo_operator_t op) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_operator == op) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_operator = op; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-operator\n", + _operator_to_string (op)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_fill_rule (cairo_script_surface_t *surface, + cairo_fill_rule_t fill_rule) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_fill_rule == fill_rule) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_fill_rule = fill_rule; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-fill-rule\n", + _fill_rule_to_string (fill_rule)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_tolerance (cairo_script_surface_t *surface, + double tolerance, + cairo_bool_t force) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (! force && surface->cr.current_tolerance == tolerance) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_tolerance = tolerance; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set-tolerance\n", + tolerance); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_antialias (cairo_script_surface_t *surface, + cairo_antialias_t antialias) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_antialias == antialias) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_antialias = antialias; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-antialias\n", + _antialias_to_string (antialias)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_width (cairo_script_surface_t *surface, + double line_width, + cairo_bool_t force) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (! force && surface->cr.current_style.line_width == line_width) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_width = line_width; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set-line-width\n", + line_width); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_cap (cairo_script_surface_t *surface, + cairo_line_cap_t line_cap) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_style.line_cap == line_cap) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_cap = line_cap; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-line-cap\n", + _line_cap_to_string (line_cap)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_join (cairo_script_surface_t *surface, + cairo_line_join_t line_join) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_style.line_join == line_join) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_join = line_join; + + _cairo_output_stream_printf (surface->ctx->stream, + "//%s set-line-join\n", + _line_join_to_string (line_join)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_miter_limit (cairo_script_surface_t *surface, + double miter_limit, + cairo_bool_t force) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (! force && surface->cr.current_style.miter_limit == miter_limit) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.miter_limit = miter_limit; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set-miter-limit\n", + miter_limit); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_dash (cairo_script_surface_t *surface, + const double *dash, + unsigned int num_dashes, + double offset, + cairo_bool_t force) +{ + unsigned int n; + + assert (_cairo_script_surface_owns_context (surface)); + + if (force && + num_dashes == 0 && + surface->cr.current_style.num_dashes == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + if (! force && + (surface->cr.current_style.num_dashes == num_dashes && + (num_dashes == 0 || + (surface->cr.current_style.dash_offset == offset && + memcmp (surface->cr.current_style.dash, dash, + sizeof (double) * num_dashes))))) + { + return CAIRO_STATUS_SUCCESS; + } + + + if (num_dashes) { + surface->cr.current_style.dash = _cairo_realloc_ab + (surface->cr.current_style.dash, + num_dashes, + sizeof (double)); + memcpy (surface->cr.current_style.dash, dash, + sizeof (double) * num_dashes); + } else { + if (surface->cr.current_style.dash != NULL) { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + } + + surface->cr.current_style.num_dashes = num_dashes; + surface->cr.current_style.dash_offset = offset; + + _cairo_output_stream_printf (surface->ctx->stream, "["); + for (n = 0; n < num_dashes; n++) { + _cairo_output_stream_printf (surface->ctx->stream, "%f", dash[n]); + if (n < num_dashes-1) + _cairo_output_stream_puts (surface->ctx->stream, " "); + } + _cairo_output_stream_printf (surface->ctx->stream, + "] %f set-dash\n", + offset); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_stroke_style (cairo_script_surface_t *surface, + const cairo_stroke_style_t *style, + cairo_bool_t force) +{ + cairo_status_t status; + + assert (_cairo_script_surface_owns_context (surface)); + + status = _emit_line_width (surface, style->line_width, force); + if (unlikely (status)) + return status; + + status = _emit_line_cap (surface, style->line_cap); + if (unlikely (status)) + return status; + + status = _emit_line_join (surface, style->line_join); + if (unlikely (status)) + return status; + + status = _emit_miter_limit (surface, style->miter_limit, force); + if (unlikely (status)) + return status; + + status = _emit_dash (surface, + style->dash, style->num_dashes, style->dash_offset, + force); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_format_to_string (cairo_format_t format) +{ + static const char *names[] = { + "ARGB32", /* CAIRO_FORMAT_ARGB32 */ + "RGB24", /* CAIRO_FORMAT_RGB24 */ + "A8", /* CAIRO_FORMAT_A8 */ + "A1" /* CAIRO_FORMAT_A1 */ + }; + assert (format < ARRAY_LENGTH (names)); + return names[format]; +} + +static cairo_status_t +_emit_solid_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + if (solid->content & CAIRO_CONTENT_ALPHA && + ! CAIRO_COLOR_IS_OPAQUE (&solid->color)) + { + if (! (solid->content & CAIRO_CONTENT_COLOR) || + (solid->color.red_short == 0 && + solid->color.green_short == 0 && + solid->color.blue_short == 0)) + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f a", + solid->color.alpha); + } + else + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f rgba", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + } + } + else + { + if (solid->color.red_short == solid->color.green_short && + solid->color.red_short == solid->color.blue_short) + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f g", + solid->color.red); + } + else + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f rgb", + solid->color.red, + solid->color.green, + solid->color.blue); + } + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, + cairo_output_stream_t *output) +{ + unsigned int n; + + for (n = 0; n < gradient->n_stops; n++) { + _cairo_output_stream_printf (output, + " %f %f %f %f %f add-color-stop\n ", + gradient->stops[n].offset, + gradient->stops[n].color.red, + gradient->stops[n].color.green, + gradient->stops[n].color.blue, + gradient->stops[n].color.alpha); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_linear_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_linear_pattern_t *linear; + + linear = (cairo_linear_pattern_t *) pattern; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f linear\n ", + _cairo_fixed_to_double (linear->p1.x), + _cairo_fixed_to_double (linear->p1.y), + _cairo_fixed_to_double (linear->p2.x), + _cairo_fixed_to_double (linear->p2.y)); + return _emit_gradient_color_stops (&linear->base, surface->ctx->stream); +} + +static cairo_status_t +_emit_radial_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_radial_pattern_t *radial; + + radial = (cairo_radial_pattern_t *) pattern; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f %f %f radial\n ", + _cairo_fixed_to_double (radial->c1.x), + _cairo_fixed_to_double (radial->c1.y), + _cairo_fixed_to_double (radial->r1), + _cairo_fixed_to_double (radial->c2.x), + _cairo_fixed_to_double (radial->c2.y), + _cairo_fixed_to_double (radial->r2)); + return _emit_gradient_color_stops (&radial->base, surface->ctx->stream); +} + +static cairo_status_t +_emit_meta_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + cairo_surface_t *null_surface; + cairo_surface_t *analysis_surface; + cairo_surface_t *similar; + cairo_status_t status; + cairo_box_t bbox; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + /* first measure the extents */ + null_surface = _cairo_null_surface_create (source->content); + analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); + cairo_surface_destroy (null_surface); + + status = analysis_surface->status; + if (unlikely (status)) + return status; + + status = _cairo_meta_surface_replay (source, analysis_surface); + _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); + cairo_surface_destroy (analysis_surface); + if (unlikely (status)) + return status; + + similar = cairo_surface_create_similar (&surface->base, + source->content, + _cairo_fixed_to_double (bbox.p2.x-bbox.p1.x), + _cairo_fixed_to_double (bbox.p2.y-bbox.p1.y)); + if (similar->status) + return similar->status; + + status = _cairo_meta_surface_replay (source, similar); + if (unlikely (status)) { + cairo_surface_destroy (similar); + return status; + } + + status = _emit_context (surface); + if (unlikely (status)) { + cairo_surface_destroy (similar); + return status; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "s%lu pattern\n ", + ((cairo_script_surface_t *) similar)->id); + cairo_surface_destroy (similar); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_script_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_script_surface_t *source; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = (cairo_script_surface_t *) surface_pattern->surface; + + _cairo_output_stream_printf (surface->ctx->stream, + "s%lu pattern\n ", source->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_image_surface (cairo_output_stream_t *output, + const cairo_image_surface_t *image) +{ + int stride, row, width; + uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *rowdata; + uint8_t *data; + + stride = image->stride; + width = image->width; + data = image->data; +#if WORDS_BIGENDIAN + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + int col; + rowdata = data; + for (col = width; col--; ) { + _cairo_output_stream_write (output, rowdata, 3); + rowdata+=4; + } + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 4*width); + data += stride; + } + break; + default: + ASSERT_NOT_REACHED; + break; + } +#else + if (stride > ARRAY_LENGTH (row_stack)) { + rowdata = malloc (stride); + if (unlikely (rowdata == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + rowdata = row_stack; + + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + int col; + for (col = 0; col < (width + 7)/8; col++) + rowdata[col] = CAIRO_BITSWAP8 (data[col]); + _cairo_output_stream_write (output, rowdata, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + uint8_t *src = data; + int col; + for (col = 0; col < width; col++) { + rowdata[3*col+2] = *src++; + rowdata[3*col+1] = *src++; + rowdata[3*col+0] = *src++; + src++; + } + _cairo_output_stream_write (output, rowdata, 3*width); + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + uint32_t *src = (uint32_t *) data; + uint32_t *dst = (uint32_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_32 (src[col]); + _cairo_output_stream_write (output, rowdata, 4*width); + data += stride; + } + break; + default: + ASSERT_NOT_REACHED; + break; + } + if (rowdata != row_stack) + free (rowdata); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_emit_png_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *base85_stream; + cairo_status_t status; + const uint8_t *mime_data; + unsigned int mime_data_length; + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /width %d set\n" + " /height %d set\n" + " /format //%s set\n" + " /mime-type (image/png) set\n" + " /source <~", + image->width, image->height, + _format_to_string (image->format)); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set\n image"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_image_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + const uint8_t *mime_data; + unsigned int mime_data_length; + + status = _emit_png_surface (surface, image); + if (_cairo_status_is_error (status)) { + return status; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /width %d set\n" + " /height %d set\n" + " /format //%s set\n" + " /source <~", + image->width, image->height, + _format_to_string (image->format)); + + if (image->width * image->height > 8) { + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + status = _write_image_surface (zlib_stream, image); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " /deflate filter set\n image"); + } else { + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + status = _write_image_surface (base85_stream, image); + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_puts (surface->ctx->stream, + " set\n image"); + } + } + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (surface->ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JPEG); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set-mime-data\n "); + } + + _cairo_output_stream_puts (surface->ctx->stream, + " pattern\n "); + + return status; +} + +static cairo_status_t +_emit_image_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + /* XXX snapshot-cow */ + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); + if (unlikely (status)) + return status; + + status = _emit_image_surface (surface, image); + + _cairo_surface_release_source_image (source, image, image_extra); + + return status; +} + +static cairo_status_t +_emit_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + switch ((int) source->type) { + case CAIRO_INTERNAL_SURFACE_TYPE_META: + return _emit_meta_surface_pattern (surface, pattern); + case CAIRO_SURFACE_TYPE_SCRIPT: + return _emit_script_surface_pattern (surface, pattern); + default: + return _emit_image_surface_pattern (surface, pattern); + } +} + +static cairo_status_t +_emit_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + /* solid colors do not need filter/extend/matrix */ + return _emit_solid_pattern (surface, pattern); + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _emit_linear_pattern (surface, pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _emit_radial_pattern (surface, pattern); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _emit_surface_pattern (surface, pattern); + break; + + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pattern->matrix)) { + _cairo_output_stream_printf (surface->ctx->stream, + " [%f %f %f %f %f %f] set-matrix\n ", + pattern->matrix.xx, pattern->matrix.yx, + pattern->matrix.xy, pattern->matrix.yy, + pattern->matrix.x0, pattern->matrix.y0); + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s set-extend\n " + " //%s set-filter\n ", + _extend_to_string (pattern->extend), + _filter_to_string (pattern->filter)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_identity (cairo_script_surface_t *surface, + cairo_bool_t *matrix_updated) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_puts (surface->ctx->stream, + "identity set-matrix\n"); + + *matrix_updated = TRUE; + cairo_matrix_init_identity (&surface->cr.current_ctm); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_source (cairo_script_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) +{ + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + assert (_cairo_script_surface_owns_context (surface)); + + if (op == CAIRO_OPERATOR_CLEAR) { + /* the source is ignored, so don't change it */ + return CAIRO_STATUS_SUCCESS; + } + + if (surface->cr.current_source == source) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_pattern_equal (surface->cr.current_source, source)) + return CAIRO_STATUS_SUCCESS; + + cairo_pattern_destroy (surface->cr.current_source); + status = _cairo_pattern_create_copy (&surface->cr.current_source, + source); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_pattern (surface, source); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set-source\n"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_move_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_line_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_output_stream_printf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_close (void *closure) +{ + _cairo_output_stream_printf (closure, + " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_path (cairo_script_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + cairo_status_t status; + + assert (_cairo_script_surface_owns_context (surface)); + assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); + + if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_fini (&surface->cr.current_path); + + _cairo_output_stream_puts (surface->ctx->stream, "n"); + + if (path == NULL) { + _cairo_path_fixed_init (&surface->cr.current_path); + } else if (_cairo_path_fixed_is_rectangle (path, &box)) { + double x1 = _cairo_fixed_to_double (box.p1.x); + double y1 = _cairo_fixed_to_double (box.p1.y); + double x2 = _cairo_fixed_to_double (box.p2.x); + double y2 = _cairo_fixed_to_double (box.p2.y); + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + " %f %f %f %f rectangle", + x1, y1, x2 - x1, y2 - y1); + } else { + cairo_status_t status; + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _path_move_to, + _path_line_to, + _path_curve_to, + _path_close, + surface->ctx->stream); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_puts (surface->ctx->stream, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *ctm, + cairo_bool_t *matrix_updated) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (memcmp (&surface->cr.current_ctm, ctm, sizeof (cairo_matrix_t)) == 0) + return CAIRO_STATUS_SUCCESS; + + *matrix_updated = TRUE; + surface->cr.current_ctm = *ctm; + + if (_cairo_matrix_is_identity (ctm)) { + _cairo_output_stream_puts (surface->ctx->stream, + "identity set-matrix\n"); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f %f %f] set-matrix\n", + ctm->xx, ctm->yx, + ctm->xy, ctm->yy, + ctm->x0, ctm->y0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_font_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *font_matrix) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (memcmp (&surface->cr.current_font_matrix, + font_matrix, + sizeof (cairo_matrix_t)) == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_font_matrix = *font_matrix; + + if (_cairo_matrix_is_identity (font_matrix)) { + _cairo_output_stream_puts (surface->ctx->stream, + "identity set-font-matrix\n"); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f %f %f] set-font-matrix\n", + font_matrix->xx, font_matrix->yx, + font_matrix->xy, font_matrix->yy, + font_matrix->x0, font_matrix->y0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static cairo_surface_t * +_cairo_script_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_script_surface_t *surface, *other; + cairo_script_vmcontext_t *ctx; + cairo_status_t status; + + other = abstract_surface; + ctx = other->ctx; + + if (other->id == (unsigned long) -1) { + cairo_status_t status; + + status = _bitmap_next_id (&ctx->surface_id, + &other->id); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + _cairo_output_stream_printf (ctx->stream, + "dict\n" + " /width %f set\n" + " /height %f set\n" + " surface dup /s%lu exch def\n" + "context /c%lu exch def\n", + other->width, + other->height, + other->id, + other->id); + } + + + surface = _cairo_script_surface_create_internal (ctx, width, height); + if (surface->base.status) + return &surface->base; + + status = _bitmap_next_id (&ctx->surface_id, + &surface->id); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + if (ctx->current_target != NULL) + _cairo_output_stream_printf (ctx->stream, "pop\n"); + + _cairo_output_stream_printf (ctx->stream, + "s%lu %u %u //%s similar dup /s%lu exch def\n" + "context dup /c%lu exch def\n", + other->id, width, height, + _content_to_string (content), + surface->id, + surface->id); + + ctx->current_target = surface; + + return &surface->base; +} + +static cairo_status_t +_vmcontext_destroy (cairo_script_vmcontext_t *ctx) +{ + cairo_status_t status; + + if (--ctx->ref) + return _cairo_output_stream_flush (ctx->stream); + + while (ctx->fonts != NULL ){ + cairo_script_surface_font_private_t *font = ctx->fonts; + ctx->fonts = font->next; + _cairo_script_surface_scaled_font_fini (font->parent); + } + + status = _cairo_output_stream_destroy (ctx->stream); + + free (ctx); + + return status; +} + +static cairo_status_t +_cairo_script_surface_finish (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + cairo_pattern_destroy (surface->cr.current_source); + _cairo_path_fixed_fini (&surface->cr.current_path); + + if (surface->ctx->current_target == surface) { + _cairo_output_stream_printf (surface->ctx->stream, + "pop\n"); + surface->ctx->current_target = NULL; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "/c%lu undef\n" + "/s%lu undef\n", + surface->id, + surface->id); + + _bitmap_release_id (&surface->ctx->surface_id, surface->id); + + status = _vmcontext_destroy (surface->ctx); + + return status; +} + +static cairo_int_status_t +_cairo_script_surface_copy_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "copy-page\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_show_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "show-page\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_intersect_clip_path (void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + if (path == NULL) { + _cairo_output_stream_puts (surface->ctx->stream, "reset-clip\n"); + return CAIRO_STATUS_SUCCESS; + } + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + return status; + + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "clip+\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + "paint\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_pattern (surface, mask); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " mask\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const 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_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_matrix (surface, ctm, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + status = _emit_stroke_style (surface, style, matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "stroke+\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + return status; + + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + + status = _emit_path (surface, path); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "fill+\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_script_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static const char * +_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) +{ + static const char *names[] = { + "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ + "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ + "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ + "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ + "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ + }; + return names[subpixel_order]; +} +static const char * +_hint_style_to_string (cairo_hint_style_t hint_style) +{ + static const char *names[] = { + "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ + "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ + "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ + "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ + "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ + }; + return names[hint_style]; +} +static const char * +_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) +{ + static const char *names[] = { + "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ + "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ + "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ + }; + return names[hint_metrics]; +} + +static cairo_status_t +_emit_font_options (cairo_script_surface_t *surface, + cairo_font_options_t *font_options) +{ + if (cairo_font_options_equal (&surface->cr.current_font_options, + font_options)) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (surface->ctx->stream, "dict\n"); + + if (font_options->antialias != surface->cr.current_font_options.antialias) { + _cairo_output_stream_printf (surface->ctx->stream, + " /antialias //%s set\n", + _antialias_to_string (font_options->antialias)); + } + + if (font_options->subpixel_order != + surface->cr.current_font_options.subpixel_order) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /subpixel-order //%s set\n", + _subpixel_order_to_string (font_options->subpixel_order)); + } + + if (font_options->hint_style != + surface->cr.current_font_options.hint_style) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /hint-style //%s set\n", + _hint_style_to_string (font_options->hint_style)); + } + + if (font_options->hint_metrics != + surface->cr.current_font_options.hint_metrics) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /hint-metrics //%s set\n", + _hint_metrics_to_string (font_options->hint_metrics)); + } + + _cairo_output_stream_printf (surface->ctx->stream, + " set-font-options\n"); + + surface->cr.current_font_options = *font_options; + + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + font_private = scaled_font->surface_private; + if (font_private != NULL) { + _cairo_output_stream_printf (font_private->ctx->stream, + "/f%lu undef\n", + font_private->id); + + _bitmap_release_id (&font_private->ctx->font_id, font_private->id); + + if (font_private->prev != NULL) + font_private->prev = font_private->next; + else + font_private->ctx->fonts = font_private->next; + + if (font_private->next != NULL) + font_private->next = font_private->prev; + + free (font_private); + + scaled_font->surface_private = NULL; + } +} + +static cairo_status_t +_emit_type42_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_script_surface_font_private_t *font_private; + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + unsigned int load_flags; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL); + if (unlikely (status)) { + free (buf); + return status; + } + + load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /type 42 set\n" + " /size %lu set\n" + " /index 0 set\n" + " /flags %d set\n" + " /source <~", + size, load_flags); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + font_private = scaled_font->surface_private; + _cairo_output_stream_printf (surface->ctx->stream, + " /deflate filter set\n" + " font dup /f%lu exch def set-font-face\n", + font_private->id); + + return status; +} + +static cairo_status_t +_emit_scaled_font_init (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + + font_private = malloc (sizeof (cairo_script_surface_font_private_t)); + if (unlikely (font_private == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->ctx = surface->ctx; + font_private->parent = scaled_font; + font_private->subset_glyph_index = 0; + font_private->has_sfnt = TRUE; + + font_private->next = font_private->ctx->fonts; + font_private->prev = NULL; + if (font_private->ctx->fonts != NULL) + font_private->ctx->fonts->prev = font_private; + font_private->ctx->fonts = font_private; + + status = _bitmap_next_id (&surface->ctx->font_id, + &font_private->id); + if (unlikely (status)) { + free (font_private); + return status; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &_cairo_script_surface_backend; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_type42_font (surface, scaled_font); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + font_private->has_sfnt = FALSE; + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /type 3 set\n" + " /metrics [%f %f %f %f %f] set\n" + " /glyphs array set\n" + " font dup /f%lu exch def set-font-face\n", + scaled_font->fs_extents.ascent, + scaled_font->fs_extents.descent, + scaled_font->fs_extents.height, + scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.max_y_advance, + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_matrix_t matrix; + cairo_font_options_t options; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_script_surface_font_private_t *font_private; + + cairo_scaled_font_get_ctm (scaled_font, &matrix); + status = _emit_matrix (surface, &matrix, &matrix_updated); + if (unlikely (status)) + return status; + + if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) + return CAIRO_STATUS_SUCCESS; + + cairo_scaled_font_get_font_matrix (scaled_font, &matrix); + status = _emit_font_matrix (surface, &matrix); + if (unlikely (status)) + return status; + + cairo_scaled_font_get_font_options (scaled_font, &options); + status = _emit_font_options (surface, &options); + if (unlikely (status)) + return status; + + surface->cr.current_scaled_font = scaled_font; + + assert (scaled_font->surface_backend == NULL || + scaled_font->surface_backend == &_cairo_script_surface_backend); + + font_private = scaled_font->surface_private; + if (font_private == NULL) { + status = _emit_scaled_font_init (surface, scaled_font); + if (unlikely (status)) + return status; + } else { + status = _emit_context (surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + "f%lu set-font-face\n", + font_private->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_vector (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_surface_font_private_t *font_private; + cairo_script_implicit_context_t old_cr; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (surface->ctx->stream, + "%lu dict\n" + " /metrics [%f %f %f %f %f %f] set\n" + " /render {\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance); + + if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f %f %f] transform\n", + scaled_font->scale_inverse.xx, + scaled_font->scale_inverse.yx, + scaled_font->scale_inverse.xy, + scaled_font->scale_inverse.yy, + scaled_font->scale_inverse.x0, + scaled_font->scale_inverse.y0); + } + + old_cr = surface->cr; + _cairo_script_implicit_context_init (&surface->cr); + status = _cairo_meta_surface_replay (scaled_glyph->meta_surface, + &surface->base); + surface->cr = old_cr; + + _cairo_output_stream_puts (surface->ctx->stream, "} set set\n"); + + return status; +} + +static cairo_status_t +_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (surface->ctx->stream, + "%lu dict\n" + " /metrics [%f %f %f %f %f %f] set\n" + " /render {\n" + "%f %f translate\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing); + + status = _emit_image_surface (surface, scaled_glyph->surface); + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { + _cairo_output_stream_printf (surface->ctx->stream, + " [%f %f %f %f %f %f] set-matrix\n", + scaled_font->font_matrix.xx, + scaled_font->font_matrix.yx, + scaled_font->font_matrix.xy, + scaled_font->font_matrix.yy, + scaled_font->font_matrix.x0, + scaled_font->font_matrix.y0); + } + _cairo_output_stream_puts (surface->ctx->stream, + "mask\n} set set\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_prologue (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + assert (scaled_font->surface_backend == &_cairo_script_surface_backend); + + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (surface->ctx->stream, + "f%lu /glyphs get\n", + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyphs (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + unsigned int num_glyphs) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned int n; + cairo_bool_t have_glyph_prologue = FALSE; + + if (num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + font_private = scaled_font->surface_private; + if (font_private->has_sfnt) + return CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (scaled_font); + for (n = 0; n < num_glyphs; n++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + if (scaled_glyph->surface_private != NULL) + continue; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_META_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_vector (surface, + scaled_font, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_bitmap (surface, + scaled_font, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (have_glyph_prologue) { + _cairo_output_stream_puts (surface->ctx->stream, "pop pop\n"); + } + + return status; +} + +static cairo_int_status_t +_cairo_script_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t backward, + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_surface_font_private_t *font_private; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t matrix; + cairo_status_t status; + double x, y, ix, iy; + int n; + cairo_output_stream_t *base85_stream = NULL; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + status = _emit_operator (surface, op); + if (unlikely (status)) + return status; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + return status; + + status = _emit_scaled_font (surface, scaled_font); + if (unlikely (status)) + return status; + + status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + return status; + + /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ + /* [cx cy [glyphs]] show_glyphs */ + + if (utf8 != NULL && clusters != NULL) { + _cairo_output_stream_printf (surface->ctx->stream, + "(%s) ", + utf8); + } + + matrix = surface->cr.current_ctm; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + ix = x = glyphs[0].x; + iy = y = glyphs[0].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + + _cairo_scaled_font_freeze_cache (scaled_font); + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f ", + ix, iy); + + for (n = 0; n < num_glyphs; n++) { + if (font_private->has_sfnt) { + if (glyphs[n].index > 256) + break; + } else { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (scaled_font); + return status; + } + } + } + + if (n == num_glyphs) { + _cairo_output_stream_puts (surface->ctx->stream, "<~"); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else + _cairo_output_stream_puts (surface->ctx->stream, "["); + + for (n = 0; n < num_glyphs; n++) { + double dx, dy; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { + ix = x = glyphs[n].x; + iy = y = glyphs[n].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " %f %f <~", + ix, iy); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + " ] %f %f [ ", + ix, iy); + } + } + if (base85_stream != NULL) { + uint8_t c; + + if (font_private->has_sfnt) + c = glyphs[n].index; + else + c = (uint8_t) (long unsigned) scaled_glyph->surface_private; + + _cairo_output_stream_write (base85_stream, &c, 1); + } else { + if (font_private->has_sfnt) + _cairo_output_stream_printf (surface->ctx->stream, " %lu", + glyphs[n].index); + else + _cairo_output_stream_printf (surface->ctx->stream, " %lu", + (long unsigned) scaled_glyph->surface_private); + } + + dx = scaled_glyph->metrics.x_advance; + dy = scaled_glyph->metrics.y_advance; + cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); + x += dx; + y += dy; + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (base85_stream != NULL) { + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } else { + _cairo_output_stream_puts (surface->ctx->stream, " ]"); + } + if (unlikely (status)) + return status; + + if (utf8 != NULL && clusters != NULL) { + for (n = 0; n < num_clusters; n++) { + if (clusters[n].num_bytes > UCHAR_MAX || + clusters[n].num_glyphs > UCHAR_MAX) + { + break; + } + } + + if (n < num_clusters) { + _cairo_output_stream_puts (surface->ctx->stream, "] [ "); + for (n = 0; n < num_clusters; n++) { + _cairo_output_stream_printf (surface->ctx->stream, + "%d %d ", + clusters[n].num_bytes, + clusters[n].num_glyphs); + } + _cairo_output_stream_puts (surface->ctx->stream, "]"); + } + else + { + _cairo_output_stream_puts (surface->ctx->stream, "] <~"); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + for (n = 0; n < num_clusters; n++) { + uint8_t c[2]; + c[0] = clusters[n].num_bytes; + c[1] = clusters[n].num_glyphs; + _cairo_output_stream_write (base85_stream, c, 2); + } + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s show-text-glyphs\n", + _direction_to_string (backward)); + } else { + _cairo_output_stream_puts (surface->ctx->stream, + "] show-glyphs\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (surface->width < 0 || surface->height < 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t +_cairo_script_surface_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + _cairo_script_surface_create_similar, + _cairo_script_surface_finish, + NULL, //_cairo_script_surface_acquire_source_image, + NULL, //_cairo_script_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + _cairo_script_surface_copy_page, + _cairo_script_surface_show_page, + NULL, /* set_clip_region */ + _cairo_script_surface_intersect_clip_path, + _cairo_script_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + _cairo_script_surface_scaled_font_fini, + NULL, /* scaled_glyph_fini */ + + /* The 5 high level operations */ + _cairo_script_surface_paint, + _cairo_script_surface_mask, + _cairo_script_surface_stroke, + _cairo_script_surface_fill, + NULL, + + NULL, //_cairo_script_surface_snapshot, + + NULL, /* is_similar */ + NULL, /* reset */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ + + /* The alternate high-level text operation */ + _cairo_script_surface_has_show_text_glyphs, + _cairo_script_surface_show_text_glyphs +}; + +static cairo_bool_t +_cairo_surface_is_script (cairo_surface_t *surface) +{ + return surface->backend == &_cairo_script_surface_backend; +} + +static cairo_script_vmcontext_t * +_cairo_script_vmcontext_create (cairo_output_stream_t *stream) +{ + cairo_script_vmcontext_t *ctx; + + ctx = malloc (sizeof (cairo_script_vmcontext_t)); + if (unlikely (ctx == NULL)) + return NULL; + + memset (ctx, 0, sizeof (cairo_script_vmcontext_t)); + + ctx->stream = stream; + ctx->mode = CAIRO_SCRIPT_MODE_ASCII; + + return ctx; +} + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) +{ + cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; + cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; + cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; + cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; + _cairo_stroke_style_init (&cr->current_style); + cr->current_source = (cairo_pattern_t *) &_cairo_pattern_black.base; + _cairo_path_fixed_init (&cr->current_path); + cairo_matrix_init_identity (&cr->current_ctm); + cairo_matrix_init_identity (&cr->current_font_matrix); + _cairo_font_options_init_default (&cr->current_font_options); + cr->current_scaled_font = NULL; +} + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx, + double width, + double height) +{ + cairo_script_surface_t *surface; + + if (unlikely (ctx == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = malloc (sizeof (cairo_script_surface_t)); + if (unlikely (surface == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_script_surface_backend, + CAIRO_CONTENT_COLOR_ALPHA); + + surface->ctx = ctx; + ctx->ref++; + + surface->width = width; + surface->height = height; + + surface->id = (unsigned long) -1; + + _cairo_script_implicit_context_init (&surface->cr); + + return surface; +} + +cairo_surface_t * +cairo_script_surface_create (const char *filename, + double width, + double height) +{ + cairo_output_stream_t *stream; + cairo_script_surface_t *surface; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + + surface = _cairo_script_surface_create_internal + (_cairo_script_vmcontext_create (stream), width, height); + if (surface->base.status) + return &surface->base; + + _cairo_output_stream_puts (surface->ctx->stream, "%!CairoScript\n"); + return &surface->base; +} + +cairo_surface_t * +cairo_script_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return &_cairo_script_surface_create_internal + (_cairo_script_vmcontext_create (stream), width, height)->base; +} + +void +cairo_script_surface_set_mode (cairo_surface_t *abstract_surface, + cairo_script_mode_t mode) +{ + cairo_script_surface_t *surface; + cairo_status_t status_ignored; + + if (! _cairo_surface_is_script (abstract_surface)) { + status_ignored = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + if (abstract_surface->status) + return; + + surface = (cairo_script_surface_t *) abstract_surface; + surface->ctx->mode = mode; +} + +cairo_script_mode_t +cairo_script_surface_get_mode (cairo_surface_t *abstract_surface) +{ + cairo_script_surface_t *surface; + + if (! _cairo_surface_is_script (abstract_surface) || + abstract_surface->status) + { + return CAIRO_SCRIPT_MODE_ASCII; + } + + surface = (cairo_script_surface_t *) abstract_surface; + return surface->ctx->mode; +} diff --git a/src/cairo-script.h b/src/cairo-script.h new file mode 100644 index 0000000..9c428e3 --- /dev/null +++ b/src/cairo-script.h @@ -0,0 +1,74 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * 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 Chris Wilson + * + * Contributor(s): + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#ifndef CAIRO_SCRIPT_H +#define CAIRO_SCRIPT_H + +#include "cairo.h" + +#if CAIRO_HAS_SCRIPT_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_script_surface_create (const char *filename, + double width, + double height); + +cairo_public cairo_surface_t * +cairo_script_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height); + +typedef enum { + CAIRO_SCRIPT_MODE_BINARY, + CAIRO_SCRIPT_MODE_ASCII +} cairo_script_mode_t; + +cairo_public void +cairo_script_surface_set_mode (cairo_surface_t *surface, + cairo_script_mode_t mode); + +cairo_public cairo_script_mode_t +cairo_script_surface_get_mode (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_SCRIPT_SURFACE*/ +# error Cairo was not compiled with support for the CairoScript backend +#endif /*CAIRO_HAS_SCRIPT_SURFACE*/ + +#endif /*CAIRO_SCRIPT_H*/ diff --git a/src/cairo-skiplist-private.h b/src/cairo-skiplist-private.h index 3a948af..250b5a2 100644 --- a/src/cairo-skiplist-private.h +++ b/src/cairo-skiplist-private.h @@ -71,6 +71,8 @@ typedef struct _skip_list { skip_elt_t *chains[MAX_LEVEL]; skip_elt_t *freelists[MAX_FREELIST_LEVEL]; int max_level; + struct pool *pool; + char pool_embedded[1024]; } cairo_skip_list_t; /* Initialize a new skip list. The compare function accepts a pointer @@ -83,8 +85,8 @@ typedef struct _skip_list { */ cairo_private void _cairo_skip_list_init (cairo_skip_list_t *list, - cairo_skip_list_compare_t compare, - size_t elt_size); + cairo_skip_list_compare_t compare, + size_t elt_size); /* Deallocate resources associated with a skip list and all elements diff --git a/src/cairo-skiplist.c b/src/cairo-skiplist.c index e2b793e..18d69ca 100644 --- a/src/cairo-skiplist.c +++ b/src/cairo-skiplist.c @@ -25,210 +25,72 @@ #include "cairo-skiplist-private.h" +#if HAVE_FFS +#include <strings.h> /* ffs() */ +#endif + #define ELT_DATA(elt) (void *) ((char*) (elt) - list->data_size) #define NEXT_TO_ELT(next) (skip_elt_t *) ((char *) (next) - offsetof (skip_elt_t, next)) -/* Four 256 element lookup tables back to back implementing a linear - * feedback shift register of degree 32. */ -static unsigned const _cairo_lfsr_random_lut[1024] = { - 0x00000000, 0x9a795537, 0xae8bff59, 0x34f2aa6e, 0xc76eab85, 0x5d17feb2, - 0x69e554dc, 0xf39c01eb, 0x14a4023d, 0x8edd570a, 0xba2ffd64, 0x2056a853, - 0xd3caa9b8, 0x49b3fc8f, 0x7d4156e1, 0xe73803d6, 0x2948047a, 0xb331514d, - 0x87c3fb23, 0x1dbaae14, 0xee26afff, 0x745ffac8, 0x40ad50a6, 0xdad40591, - 0x3dec0647, 0xa7955370, 0x9367f91e, 0x091eac29, 0xfa82adc2, 0x60fbf8f5, - 0x5409529b, 0xce7007ac, 0x529008f4, 0xc8e95dc3, 0xfc1bf7ad, 0x6662a29a, - 0x95fea371, 0x0f87f646, 0x3b755c28, 0xa10c091f, 0x46340ac9, 0xdc4d5ffe, - 0xe8bff590, 0x72c6a0a7, 0x815aa14c, 0x1b23f47b, 0x2fd15e15, 0xb5a80b22, - 0x7bd80c8e, 0xe1a159b9, 0xd553f3d7, 0x4f2aa6e0, 0xbcb6a70b, 0x26cff23c, - 0x123d5852, 0x88440d65, 0x6f7c0eb3, 0xf5055b84, 0xc1f7f1ea, 0x5b8ea4dd, - 0xa812a536, 0x326bf001, 0x06995a6f, 0x9ce00f58, 0xa52011e8, 0x3f5944df, - 0x0babeeb1, 0x91d2bb86, 0x624eba6d, 0xf837ef5a, 0xccc54534, 0x56bc1003, - 0xb18413d5, 0x2bfd46e2, 0x1f0fec8c, 0x8576b9bb, 0x76eab850, 0xec93ed67, - 0xd8614709, 0x4218123e, 0x8c681592, 0x161140a5, 0x22e3eacb, 0xb89abffc, - 0x4b06be17, 0xd17feb20, 0xe58d414e, 0x7ff41479, 0x98cc17af, 0x02b54298, - 0x3647e8f6, 0xac3ebdc1, 0x5fa2bc2a, 0xc5dbe91d, 0xf1294373, 0x6b501644, - 0xf7b0191c, 0x6dc94c2b, 0x593be645, 0xc342b372, 0x30deb299, 0xaaa7e7ae, - 0x9e554dc0, 0x042c18f7, 0xe3141b21, 0x796d4e16, 0x4d9fe478, 0xd7e6b14f, - 0x247ab0a4, 0xbe03e593, 0x8af14ffd, 0x10881aca, 0xdef81d66, 0x44814851, - 0x7073e23f, 0xea0ab708, 0x1996b6e3, 0x83efe3d4, 0xb71d49ba, 0x2d641c8d, - 0xca5c1f5b, 0x50254a6c, 0x64d7e002, 0xfeaeb535, 0x0d32b4de, 0x974be1e9, - 0xa3b94b87, 0x39c01eb0, 0xd03976e7, 0x4a4023d0, 0x7eb289be, 0xe4cbdc89, - 0x1757dd62, 0x8d2e8855, 0xb9dc223b, 0x23a5770c, 0xc49d74da, 0x5ee421ed, - 0x6a168b83, 0xf06fdeb4, 0x03f3df5f, 0x998a8a68, 0xad782006, 0x37017531, - 0xf971729d, 0x630827aa, 0x57fa8dc4, 0xcd83d8f3, 0x3e1fd918, 0xa4668c2f, - 0x90942641, 0x0aed7376, 0xedd570a0, 0x77ac2597, 0x435e8ff9, 0xd927dace, - 0x2abbdb25, 0xb0c28e12, 0x8430247c, 0x1e49714b, 0x82a97e13, 0x18d02b24, - 0x2c22814a, 0xb65bd47d, 0x45c7d596, 0xdfbe80a1, 0xeb4c2acf, 0x71357ff8, - 0x960d7c2e, 0x0c742919, 0x38868377, 0xa2ffd640, 0x5163d7ab, 0xcb1a829c, - 0xffe828f2, 0x65917dc5, 0xabe17a69, 0x31982f5e, 0x056a8530, 0x9f13d007, - 0x6c8fd1ec, 0xf6f684db, 0xc2042eb5, 0x587d7b82, 0xbf457854, 0x253c2d63, - 0x11ce870d, 0x8bb7d23a, 0x782bd3d1, 0xe25286e6, 0xd6a02c88, 0x4cd979bf, - 0x7519670f, 0xef603238, 0xdb929856, 0x41ebcd61, 0xb277cc8a, 0x280e99bd, - 0x1cfc33d3, 0x868566e4, 0x61bd6532, 0xfbc43005, 0xcf369a6b, 0x554fcf5c, - 0xa6d3ceb7, 0x3caa9b80, 0x085831ee, 0x922164d9, 0x5c516375, 0xc6283642, - 0xf2da9c2c, 0x68a3c91b, 0x9b3fc8f0, 0x01469dc7, 0x35b437a9, 0xafcd629e, - 0x48f56148, 0xd28c347f, 0xe67e9e11, 0x7c07cb26, 0x8f9bcacd, 0x15e29ffa, - 0x21103594, 0xbb6960a3, 0x27896ffb, 0xbdf03acc, 0x890290a2, 0x137bc595, - 0xe0e7c47e, 0x7a9e9149, 0x4e6c3b27, 0xd4156e10, 0x332d6dc6, 0xa95438f1, - 0x9da6929f, 0x07dfc7a8, 0xf443c643, 0x6e3a9374, 0x5ac8391a, 0xc0b16c2d, - 0x0ec16b81, 0x94b83eb6, 0xa04a94d8, 0x3a33c1ef, 0xc9afc004, 0x53d69533, - 0x67243f5d, 0xfd5d6a6a, 0x1a6569bc, 0x801c3c8b, 0xb4ee96e5, 0x2e97c3d2, - 0xdd0bc239, 0x4772970e, 0x73803d60, 0xe9f96857, 0x00000000, 0x3a0bb8f9, - 0x741771f2, 0x4e1cc90b, 0xe82ee3e4, 0xd2255b1d, 0x9c399216, 0xa6322aef, - 0x4a2492ff, 0x702f2a06, 0x3e33e30d, 0x04385bf4, 0xa20a711b, 0x9801c9e2, - 0xd61d00e9, 0xec16b810, 0x944925fe, 0xae429d07, 0xe05e540c, 0xda55ecf5, - 0x7c67c61a, 0x466c7ee3, 0x0870b7e8, 0x327b0f11, 0xde6db701, 0xe4660ff8, - 0xaa7ac6f3, 0x90717e0a, 0x364354e5, 0x0c48ec1c, 0x42542517, 0x785f9dee, - 0xb2eb1ecb, 0x88e0a632, 0xc6fc6f39, 0xfcf7d7c0, 0x5ac5fd2f, 0x60ce45d6, - 0x2ed28cdd, 0x14d93424, 0xf8cf8c34, 0xc2c434cd, 0x8cd8fdc6, 0xb6d3453f, - 0x10e16fd0, 0x2aead729, 0x64f61e22, 0x5efda6db, 0x26a23b35, 0x1ca983cc, - 0x52b54ac7, 0x68bef23e, 0xce8cd8d1, 0xf4876028, 0xba9ba923, 0x809011da, - 0x6c86a9ca, 0x568d1133, 0x1891d838, 0x229a60c1, 0x84a84a2e, 0xbea3f2d7, - 0xf0bf3bdc, 0xcab48325, 0xffaf68a1, 0xc5a4d058, 0x8bb81953, 0xb1b3a1aa, - 0x17818b45, 0x2d8a33bc, 0x6396fab7, 0x599d424e, 0xb58bfa5e, 0x8f8042a7, - 0xc19c8bac, 0xfb973355, 0x5da519ba, 0x67aea143, 0x29b26848, 0x13b9d0b1, - 0x6be64d5f, 0x51edf5a6, 0x1ff13cad, 0x25fa8454, 0x83c8aebb, 0xb9c31642, - 0xf7dfdf49, 0xcdd467b0, 0x21c2dfa0, 0x1bc96759, 0x55d5ae52, 0x6fde16ab, - 0xc9ec3c44, 0xf3e784bd, 0xbdfb4db6, 0x87f0f54f, 0x4d44766a, 0x774fce93, - 0x39530798, 0x0358bf61, 0xa56a958e, 0x9f612d77, 0xd17de47c, 0xeb765c85, - 0x0760e495, 0x3d6b5c6c, 0x73779567, 0x497c2d9e, 0xef4e0771, 0xd545bf88, - 0x9b597683, 0xa152ce7a, 0xd90d5394, 0xe306eb6d, 0xad1a2266, 0x97119a9f, - 0x3123b070, 0x0b280889, 0x4534c182, 0x7f3f797b, 0x9329c16b, 0xa9227992, - 0xe73eb099, 0xdd350860, 0x7b07228f, 0x410c9a76, 0x0f10537d, 0x351beb84, - 0x65278475, 0x5f2c3c8c, 0x1130f587, 0x2b3b4d7e, 0x8d096791, 0xb702df68, - 0xf91e1663, 0xc315ae9a, 0x2f03168a, 0x1508ae73, 0x5b146778, 0x611fdf81, - 0xc72df56e, 0xfd264d97, 0xb33a849c, 0x89313c65, 0xf16ea18b, 0xcb651972, - 0x8579d079, 0xbf726880, 0x1940426f, 0x234bfa96, 0x6d57339d, 0x575c8b64, - 0xbb4a3374, 0x81418b8d, 0xcf5d4286, 0xf556fa7f, 0x5364d090, 0x696f6869, - 0x2773a162, 0x1d78199b, 0xd7cc9abe, 0xedc72247, 0xa3dbeb4c, 0x99d053b5, - 0x3fe2795a, 0x05e9c1a3, 0x4bf508a8, 0x71feb051, 0x9de80841, 0xa7e3b0b8, - 0xe9ff79b3, 0xd3f4c14a, 0x75c6eba5, 0x4fcd535c, 0x01d19a57, 0x3bda22ae, - 0x4385bf40, 0x798e07b9, 0x3792ceb2, 0x0d99764b, 0xabab5ca4, 0x91a0e45d, - 0xdfbc2d56, 0xe5b795af, 0x09a12dbf, 0x33aa9546, 0x7db65c4d, 0x47bde4b4, - 0xe18fce5b, 0xdb8476a2, 0x9598bfa9, 0xaf930750, 0x9a88ecd4, 0xa083542d, - 0xee9f9d26, 0xd49425df, 0x72a60f30, 0x48adb7c9, 0x06b17ec2, 0x3cbac63b, - 0xd0ac7e2b, 0xeaa7c6d2, 0xa4bb0fd9, 0x9eb0b720, 0x38829dcf, 0x02892536, - 0x4c95ec3d, 0x769e54c4, 0x0ec1c92a, 0x34ca71d3, 0x7ad6b8d8, 0x40dd0021, - 0xe6ef2ace, 0xdce49237, 0x92f85b3c, 0xa8f3e3c5, 0x44e55bd5, 0x7eeee32c, - 0x30f22a27, 0x0af992de, 0xaccbb831, 0x96c000c8, 0xd8dcc9c3, 0xe2d7713a, - 0x2863f21f, 0x12684ae6, 0x5c7483ed, 0x667f3b14, 0xc04d11fb, 0xfa46a902, - 0xb45a6009, 0x8e51d8f0, 0x624760e0, 0x584cd819, 0x16501112, 0x2c5ba9eb, - 0x8a698304, 0xb0623bfd, 0xfe7ef2f6, 0xc4754a0f, 0xbc2ad7e1, 0x86216f18, - 0xc83da613, 0xf2361eea, 0x54043405, 0x6e0f8cfc, 0x201345f7, 0x1a18fd0e, - 0xf60e451e, 0xcc05fde7, 0x821934ec, 0xb8128c15, 0x1e20a6fa, 0x242b1e03, - 0x6a37d708, 0x503c6ff1, 0x00000000, 0xca4f08ea, 0x0ee744e3, 0xc4a84c09, - 0x1dce89c6, 0xd781812c, 0x1329cd25, 0xd966c5cf, 0x3b9d138c, 0xf1d21b66, - 0x357a576f, 0xff355f85, 0x26539a4a, 0xec1c92a0, 0x28b4dea9, 0xe2fbd643, - 0x773a2718, 0xbd752ff2, 0x79dd63fb, 0xb3926b11, 0x6af4aede, 0xa0bba634, - 0x6413ea3d, 0xae5ce2d7, 0x4ca73494, 0x86e83c7e, 0x42407077, 0x880f789d, - 0x5169bd52, 0x9b26b5b8, 0x5f8ef9b1, 0x95c1f15b, 0xee744e30, 0x243b46da, - 0xe0930ad3, 0x2adc0239, 0xf3bac7f6, 0x39f5cf1c, 0xfd5d8315, 0x37128bff, - 0xd5e95dbc, 0x1fa65556, 0xdb0e195f, 0x114111b5, 0xc827d47a, 0x0268dc90, - 0xc6c09099, 0x0c8f9873, 0x994e6928, 0x530161c2, 0x97a92dcb, 0x5de62521, - 0x8480e0ee, 0x4ecfe804, 0x8a67a40d, 0x4028ace7, 0xa2d37aa4, 0x689c724e, - 0xac343e47, 0x667b36ad, 0xbf1df362, 0x7552fb88, 0xb1fab781, 0x7bb5bf6b, - 0x4691c957, 0x8cdec1bd, 0x48768db4, 0x8239855e, 0x5b5f4091, 0x9110487b, - 0x55b80472, 0x9ff70c98, 0x7d0cdadb, 0xb743d231, 0x73eb9e38, 0xb9a496d2, - 0x60c2531d, 0xaa8d5bf7, 0x6e2517fe, 0xa46a1f14, 0x31abee4f, 0xfbe4e6a5, - 0x3f4caaac, 0xf503a246, 0x2c656789, 0xe62a6f63, 0x2282236a, 0xe8cd2b80, - 0x0a36fdc3, 0xc079f529, 0x04d1b920, 0xce9eb1ca, 0x17f87405, 0xddb77cef, - 0x191f30e6, 0xd350380c, 0xa8e58767, 0x62aa8f8d, 0xa602c384, 0x6c4dcb6e, - 0xb52b0ea1, 0x7f64064b, 0xbbcc4a42, 0x718342a8, 0x937894eb, 0x59379c01, - 0x9d9fd008, 0x57d0d8e2, 0x8eb61d2d, 0x44f915c7, 0x805159ce, 0x4a1e5124, - 0xdfdfa07f, 0x1590a895, 0xd138e49c, 0x1b77ec76, 0xc21129b9, 0x085e2153, - 0xccf66d5a, 0x06b965b0, 0xe442b3f3, 0x2e0dbb19, 0xeaa5f710, 0x20eafffa, - 0xf98c3a35, 0x33c332df, 0xf76b7ed6, 0x3d24763c, 0x8d2392ae, 0x476c9a44, - 0x83c4d64d, 0x498bdea7, 0x90ed1b68, 0x5aa21382, 0x9e0a5f8b, 0x54455761, - 0xb6be8122, 0x7cf189c8, 0xb859c5c1, 0x7216cd2b, 0xab7008e4, 0x613f000e, - 0xa5974c07, 0x6fd844ed, 0xfa19b5b6, 0x3056bd5c, 0xf4fef155, 0x3eb1f9bf, - 0xe7d73c70, 0x2d98349a, 0xe9307893, 0x237f7079, 0xc184a63a, 0x0bcbaed0, - 0xcf63e2d9, 0x052cea33, 0xdc4a2ffc, 0x16052716, 0xd2ad6b1f, 0x18e263f5, - 0x6357dc9e, 0xa918d474, 0x6db0987d, 0xa7ff9097, 0x7e995558, 0xb4d65db2, - 0x707e11bb, 0xba311951, 0x58cacf12, 0x9285c7f8, 0x562d8bf1, 0x9c62831b, - 0x450446d4, 0x8f4b4e3e, 0x4be30237, 0x81ac0add, 0x146dfb86, 0xde22f36c, - 0x1a8abf65, 0xd0c5b78f, 0x09a37240, 0xc3ec7aaa, 0x074436a3, 0xcd0b3e49, - 0x2ff0e80a, 0xe5bfe0e0, 0x2117ace9, 0xeb58a403, 0x323e61cc, 0xf8716926, - 0x3cd9252f, 0xf6962dc5, 0xcbb25bf9, 0x01fd5313, 0xc5551f1a, 0x0f1a17f0, - 0xd67cd23f, 0x1c33dad5, 0xd89b96dc, 0x12d49e36, 0xf02f4875, 0x3a60409f, - 0xfec80c96, 0x3487047c, 0xede1c1b3, 0x27aec959, 0xe3068550, 0x29498dba, - 0xbc887ce1, 0x76c7740b, 0xb26f3802, 0x782030e8, 0xa146f527, 0x6b09fdcd, - 0xafa1b1c4, 0x65eeb92e, 0x87156f6d, 0x4d5a6787, 0x89f22b8e, 0x43bd2364, - 0x9adbe6ab, 0x5094ee41, 0x943ca248, 0x5e73aaa2, 0x25c615c9, 0xef891d23, - 0x2b21512a, 0xe16e59c0, 0x38089c0f, 0xf24794e5, 0x36efd8ec, 0xfca0d006, - 0x1e5b0645, 0xd4140eaf, 0x10bc42a6, 0xdaf34a4c, 0x03958f83, 0xc9da8769, - 0x0d72cb60, 0xc73dc38a, 0x52fc32d1, 0x98b33a3b, 0x5c1b7632, 0x96547ed8, - 0x4f32bb17, 0x857db3fd, 0x41d5fff4, 0x8b9af71e, 0x6961215d, 0xa32e29b7, - 0x678665be, 0xadc96d54, 0x74afa89b, 0xbee0a071, 0x7a48ec78, 0xb007e492, - 0x00000000, 0x803e706b, 0x9a05b5e1, 0x1a3bc58a, 0xae723ef5, 0x2e4c4e9e, - 0x34778b14, 0xb449fb7f, 0xc69d28dd, 0x46a358b6, 0x5c989d3c, 0xdca6ed57, - 0x68ef1628, 0xe8d16643, 0xf2eaa3c9, 0x72d4d3a2, 0x1743048d, 0x977d74e6, - 0x8d46b16c, 0x0d78c107, 0xb9313a78, 0x390f4a13, 0x23348f99, 0xa30afff2, - 0xd1de2c50, 0x51e05c3b, 0x4bdb99b1, 0xcbe5e9da, 0x7fac12a5, 0xff9262ce, - 0xe5a9a744, 0x6597d72f, 0x2e86091a, 0xaeb87971, 0xb483bcfb, 0x34bdcc90, - 0x80f437ef, 0x00ca4784, 0x1af1820e, 0x9acff265, 0xe81b21c7, 0x682551ac, - 0x721e9426, 0xf220e44d, 0x46691f32, 0xc6576f59, 0xdc6caad3, 0x5c52dab8, - 0x39c50d97, 0xb9fb7dfc, 0xa3c0b876, 0x23fec81d, 0x97b73362, 0x17894309, - 0x0db28683, 0x8d8cf6e8, 0xff58254a, 0x7f665521, 0x655d90ab, 0xe563e0c0, - 0x512a1bbf, 0xd1146bd4, 0xcb2fae5e, 0x4b11de35, 0x5d0c1234, 0xdd32625f, - 0xc709a7d5, 0x4737d7be, 0xf37e2cc1, 0x73405caa, 0x697b9920, 0xe945e94b, - 0x9b913ae9, 0x1baf4a82, 0x01948f08, 0x81aaff63, 0x35e3041c, 0xb5dd7477, - 0xafe6b1fd, 0x2fd8c196, 0x4a4f16b9, 0xca7166d2, 0xd04aa358, 0x5074d333, - 0xe43d284c, 0x64035827, 0x7e389dad, 0xfe06edc6, 0x8cd23e64, 0x0cec4e0f, - 0x16d78b85, 0x96e9fbee, 0x22a00091, 0xa29e70fa, 0xb8a5b570, 0x389bc51b, - 0x738a1b2e, 0xf3b46b45, 0xe98faecf, 0x69b1dea4, 0xddf825db, 0x5dc655b0, - 0x47fd903a, 0xc7c3e051, 0xb51733f3, 0x35294398, 0x2f128612, 0xaf2cf679, - 0x1b650d06, 0x9b5b7d6d, 0x8160b8e7, 0x015ec88c, 0x64c91fa3, 0xe4f76fc8, - 0xfeccaa42, 0x7ef2da29, 0xcabb2156, 0x4a85513d, 0x50be94b7, 0xd080e4dc, - 0xa254377e, 0x226a4715, 0x3851829f, 0xb86ff2f4, 0x0c26098b, 0x8c1879e0, - 0x9623bc6a, 0x161dcc01, 0xba182468, 0x3a265403, 0x201d9189, 0xa023e1e2, - 0x146a1a9d, 0x94546af6, 0x8e6faf7c, 0x0e51df17, 0x7c850cb5, 0xfcbb7cde, - 0xe680b954, 0x66bec93f, 0xd2f73240, 0x52c9422b, 0x48f287a1, 0xc8ccf7ca, - 0xad5b20e5, 0x2d65508e, 0x375e9504, 0xb760e56f, 0x03291e10, 0x83176e7b, - 0x992cabf1, 0x1912db9a, 0x6bc60838, 0xebf87853, 0xf1c3bdd9, 0x71fdcdb2, - 0xc5b436cd, 0x458a46a6, 0x5fb1832c, 0xdf8ff347, 0x949e2d72, 0x14a05d19, - 0x0e9b9893, 0x8ea5e8f8, 0x3aec1387, 0xbad263ec, 0xa0e9a666, 0x20d7d60d, - 0x520305af, 0xd23d75c4, 0xc806b04e, 0x4838c025, 0xfc713b5a, 0x7c4f4b31, - 0x66748ebb, 0xe64afed0, 0x83dd29ff, 0x03e35994, 0x19d89c1e, 0x99e6ec75, - 0x2daf170a, 0xad916761, 0xb7aaa2eb, 0x3794d280, 0x45400122, 0xc57e7149, - 0xdf45b4c3, 0x5f7bc4a8, 0xeb323fd7, 0x6b0c4fbc, 0x71378a36, 0xf109fa5d, - 0xe714365c, 0x672a4637, 0x7d1183bd, 0xfd2ff3d6, 0x496608a9, 0xc95878c2, - 0xd363bd48, 0x535dcd23, 0x21891e81, 0xa1b76eea, 0xbb8cab60, 0x3bb2db0b, - 0x8ffb2074, 0x0fc5501f, 0x15fe9595, 0x95c0e5fe, 0xf05732d1, 0x706942ba, - 0x6a528730, 0xea6cf75b, 0x5e250c24, 0xde1b7c4f, 0xc420b9c5, 0x441ec9ae, - 0x36ca1a0c, 0xb6f46a67, 0xaccfafed, 0x2cf1df86, 0x98b824f9, 0x18865492, - 0x02bd9118, 0x8283e173, 0xc9923f46, 0x49ac4f2d, 0x53978aa7, 0xd3a9facc, - 0x67e001b3, 0xe7de71d8, 0xfde5b452, 0x7ddbc439, 0x0f0f179b, 0x8f3167f0, - 0x950aa27a, 0x1534d211, 0xa17d296e, 0x21435905, 0x3b789c8f, 0xbb46ece4, - 0xded13bcb, 0x5eef4ba0, 0x44d48e2a, 0xc4eafe41, 0x70a3053e, 0xf09d7555, - 0xeaa6b0df, 0x6a98c0b4, 0x184c1316, 0x9872637d, 0x8249a6f7, 0x0277d69c, - 0xb63e2de3, 0x36005d88, 0x2c3b9802, 0xac05e869}; - -static unsigned _cairo_lfsr_random_state = 0x12345678; - -static unsigned -lfsr_random(void) +static uint32_t +hars_petruska_f54_1_random (void) +{ +# define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x = 0; + x = (x ^ rol(x, 5) ^ rol(x, 24)) + 0x37798849; + return x; +# undef rol +} + +struct pool { + struct pool *next; + char *ptr; + unsigned int rem; +}; + +static struct pool * +pool_new (void) +{ + struct pool *pool; + + pool = malloc (8192 - 8); + if (unlikely (pool == NULL)) + return NULL; + + pool->next = NULL; + pool->rem = 8192 - 8 - sizeof (struct pool); + pool->ptr = (char *) (pool + 1); + + return pool; +} + +static void +pools_destroy (struct pool *pool) { - unsigned next; - next = _cairo_lfsr_random_lut[((_cairo_lfsr_random_state>> 0) & 0xFF) + 0*256]; - next ^= _cairo_lfsr_random_lut[((_cairo_lfsr_random_state>> 8) & 0xFF) + 1*256]; - next ^= _cairo_lfsr_random_lut[((_cairo_lfsr_random_state>>16) & 0xFF) + 2*256]; - next ^= _cairo_lfsr_random_lut[((_cairo_lfsr_random_state>>24) & 0xFF) + 3*256]; - return _cairo_lfsr_random_state = next; + while (pool->next != NULL) { + struct pool *next = pool->next; + free (pool); + pool = next; + } } /* * Initialize an empty skip list */ void -_cairo_skip_list_init (cairo_skip_list_t *list, - cairo_skip_list_compare_t compare, - size_t elt_size) +_cairo_skip_list_init (cairo_skip_list_t *list, + cairo_skip_list_compare_t compare, + size_t elt_size) { int i; list->compare = compare; list->elt_size = elt_size; list->data_size = elt_size - sizeof (skip_elt_t); + list->pool = (struct pool *) list->pool_embedded; + list->pool->next = NULL; + list->pool->rem = sizeof (list->pool_embedded) - sizeof (struct pool); + list->pool->ptr = list->pool_embedded + sizeof (struct pool); for (i = 0; i < MAX_LEVEL; i++) { list->chains[i] = NULL; @@ -244,20 +106,7 @@ _cairo_skip_list_init (cairo_skip_list_t *list, void _cairo_skip_list_fini (cairo_skip_list_t *list) { - skip_elt_t *elt; - int i; - - while ((elt = list->chains[0])) { - _cairo_skip_list_delete_given (list, elt); - } - for (i=0; i<MAX_FREELIST_LEVEL; i++) { - elt = list->freelists[i]; - while (elt) { - skip_elt_t *nextfree = elt->prev; - free (ELT_DATA(elt)); - elt = nextfree; - } - } + pools_destroy (list->pool); } /* @@ -269,20 +118,50 @@ _cairo_skip_list_fini (cairo_skip_list_t *list) static int random_level (void) { - int level = 0; /* tricky bit -- each bit is '1' 75% of the time. * This works because we only use the lower MAX_LEVEL * bits, and MAX_LEVEL < 16 */ - long int bits = lfsr_random(); - bits |= bits >> 16; - - while (++level < MAX_LEVEL) - { - if (bits & 1) - break; + uint32_t bits = hars_petruska_f54_1_random (); +#if HAVE_FFS + return ffs (-(1<<MAX_LEVEL) | bits | bits >> 16); +#else + int level = 1; + + bits |= -(1<<MAX_LEVEL) | bits >> 16; + while ((bits & 1) == 0) { + level++; bits >>= 1; } return level; +#endif +} + +static void * +pool_alloc (cairo_skip_list_t *list, + unsigned int level) +{ + unsigned int size; + struct pool *pool; + void *ptr; + + size = list->elt_size + + (FREELIST_MAX_LEVEL_FOR (level) - 1) * sizeof (skip_elt_t *); + + pool = list->pool; + if (size > pool->rem) { + pool = pool_new (); + if (unlikely (pool == NULL)) + return NULL; + + pool->next = list->pool; + list->pool = pool; + } + + ptr = pool->ptr; + pool->ptr += size; + pool->rem -= size; + + return ptr; } static void * @@ -294,8 +173,7 @@ alloc_node_for_level (cairo_skip_list_t *list, unsigned level) list->freelists[freelist_level] = elt->prev; return ELT_DATA(elt); } - return malloc (list->elt_size - + (FREELIST_MAX_LEVEL_FOR (level) - 1) * sizeof (skip_elt_t *)); + return pool_alloc (list, level); } static void @@ -359,7 +237,7 @@ _cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique) } data_and_elt = alloc_node_for_level (list, level); - if (data_and_elt == NULL) { + if (unlikely (data_and_elt == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } @@ -483,3 +361,39 @@ _cairo_skip_list_delete_given (cairo_skip_list_t *list, skip_elt_t *given) list->max_level--; free_elt (list, elt); } + +#if MAIN +typedef struct { + int n; + skip_elt_t elt; +} test_elt_t; + +static int +test_cmp (void *list, void *A, void *B) +{ + const test_elt_t *a = A, *b = B; + return a->n - b->n; +} + +int +main (void) +{ + cairo_skip_list_t list; + test_elt_t elt; + int n; + + _cairo_skip_list_init (&list, test_cmp, sizeof (test_elt_t)); + for (n = 0; n < 10000000; n++) { + void *elt_and_data; + elt.n = n; + elt_and_data = _cairo_skip_list_insert (&list, &elt, TRUE); + assert (elt_and_data != NULL); + } + _cairo_skip_list_fini (&list); + + return 0; +} + +/* required supporting stubs */ +cairo_status_t _cairo_error (cairo_status_t status) { return status; } +#endif diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h new file mode 100644 index 0000000..c285f94 --- /dev/null +++ b/src/cairo-spans-private.h @@ -0,0 +1,144 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2008 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef CAIRO_SPANS_PRIVATE_H +#define CAIRO_SPANS_PRIVATE_H +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" + +/* Number of bits of precision used for alpha. */ +#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8 +#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1) + +/* A structure representing an open-ended horizontal span of constant + * pixel coverage. */ +typedef struct _cairo_half_open_span { + /* The inclusive x-coordinate of the start of the span. */ + int x; + + /* The pixel coverage for the pixels to the right. */ + int coverage; +} cairo_half_open_span_t; + +/* Span renderer interface. Instances of renderers are provided by + * surfaces if they want to composite spans instead of trapezoids. */ +typedef struct _cairo_span_renderer cairo_span_renderer_t; +struct _cairo_span_renderer { + /* Called to destroy the renderer. */ + cairo_destroy_func_t destroy; + + /* Render the spans on row y of the source by whatever compositing + * method is required. The function should ignore spans outside + * the bounding box set by the init() function. */ + cairo_status_t (*render_row)( + void *abstract_renderer, + int y, + const cairo_half_open_span_t *coverages, + unsigned num_coverages); + + /* Called after all rows have been rendered to perform whatever + * final rendering step is required. This function is called just + * once before the renderer is destroyed. */ + cairo_status_t (*finish)( + void *abstract_renderer); + + /* Private status variable. */ + cairo_status_t status; +}; + +/* Scan converter interface. */ +typedef struct _cairo_scan_converter cairo_scan_converter_t; +struct _cairo_scan_converter { + /* Destroy this scan converter. */ + cairo_destroy_func_t destroy; + + /* Add an edge to the converter. */ + cairo_status_t + (*add_edge)( + void *abstract_converter, + cairo_fixed_t x1, + cairo_fixed_t y1, + cairo_fixed_t x2, + cairo_fixed_t y2); + + /* Generates coverage spans for rows for the added edges and calls + * the renderer function for each row. After generating spans the + * only valid thing to do with the converter is to destroy it. */ + cairo_status_t + (*generate)( + void *abstract_converter, + cairo_span_renderer_t *renderer); + + /* Private status. Read with _cairo_scan_converter_status(). */ + cairo_status_t status; +}; + +/* Scan converter constructors. */ + +cairo_private cairo_scan_converter_t * +_cairo_tor_scan_converter_create( + int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule); + +/* cairo-spans.c: */ + +cairo_private cairo_scan_converter_t * +_cairo_scan_converter_create_in_error (cairo_status_t error); + +cairo_private cairo_status_t +_cairo_scan_converter_status (void *abstract_converter); + +cairo_private cairo_status_t +_cairo_scan_converter_set_error (void *abstract_converter, + cairo_status_t error); + +cairo_private cairo_span_renderer_t * +_cairo_span_renderer_create_in_error (cairo_status_t error); + +cairo_private cairo_status_t +_cairo_span_renderer_status (void *abstract_renderer); + +/* Set the renderer into an error state. This sets all the method + * pointers except ->destroy() of the renderer to no-op + * implementations that just return the error status. */ +cairo_private cairo_status_t +_cairo_span_renderer_set_error (void *abstract_renderer, + cairo_status_t error); + +cairo_private cairo_status_t +_cairo_path_fixed_fill_using_spans ( + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_path_fixed_t *path, + cairo_surface_t *dst, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects); +#endif /* CAIRO_SPANS_PRIVATE_H */ diff --git a/src/cairo-spans.c b/src/cairo-spans.c new file mode 100644 index 0000000..625f83f --- /dev/null +++ b/src/cairo-spans.c @@ -0,0 +1,404 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2008 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "cairoint.h" + +typedef struct { + cairo_scan_converter_t *converter; + cairo_point_t current_point; + cairo_point_t first_point; + cairo_bool_t has_first_point; +} scan_converter_filler_t; + +static void +scan_converter_filler_init (scan_converter_filler_t *filler, + cairo_scan_converter_t *converter) +{ + filler->converter = converter; + filler->current_point.x = 0; + filler->current_point.y = 0; + filler->first_point = filler->current_point; + filler->has_first_point = FALSE; +} + +static cairo_status_t +scan_converter_filler_close_path (void *closure) +{ + scan_converter_filler_t *filler = closure; + cairo_status_t status; + + filler->has_first_point = FALSE; + + if (filler->first_point.x == filler->current_point.x && + filler->first_point.y == filler->current_point.y) + { + return CAIRO_STATUS_SUCCESS; + } + + status = filler->converter->add_edge ( + filler->converter, + filler->current_point.x, filler->current_point.y, + filler->first_point.x, filler->first_point.y); + + filler->current_point = filler->first_point; + return status; +} + +static cairo_status_t +scan_converter_filler_move_to (void *closure, + const cairo_point_t *p) +{ + scan_converter_filler_t *filler = closure; + if (filler->has_first_point) { + cairo_status_t status = scan_converter_filler_close_path (closure); + if (status) + return status; + } + filler->current_point.x = p->x; + filler->current_point.y = p->y; + filler->first_point = filler->current_point; + filler->has_first_point = TRUE; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +scan_converter_filler_line_to (void *closure, + const cairo_point_t *p) +{ + scan_converter_filler_t *filler = closure; + cairo_status_t status; + cairo_point_t to; + + to.x = p->x; + to.y = p->y; + + status = filler->converter->add_edge ( + filler->converter, + filler->current_point.x, filler->current_point.y, + to.x, to.y); + + filler->current_point = to; + + return status; +} + +static cairo_status_t +_cairo_path_fixed_fill_to_scan_converter ( + cairo_path_fixed_t *path, + double tolerance, + cairo_scan_converter_t *converter) +{ + scan_converter_filler_t filler; + cairo_status_t status; + + scan_converter_filler_init (&filler, converter); + + status = _cairo_path_fixed_interpret_flat ( + path, CAIRO_DIRECTION_FORWARD, + scan_converter_filler_move_to, + scan_converter_filler_line_to, + scan_converter_filler_close_path, + &filler, tolerance); + if (status) + return status; + + return scan_converter_filler_close_path (&filler); +} + +static cairo_scan_converter_t * +_create_scan_converter (cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + if (antialias == CAIRO_ANTIALIAS_NONE) { + ASSERT_NOT_REACHED; + return _cairo_scan_converter_create_in_error ( + CAIRO_INT_STATUS_UNSUPPORTED); + } + else { + return _cairo_tor_scan_converter_create ( + rects->mask.x, + rects->mask.y, + rects->mask.x + rects->width, + rects->mask.y + rects->height, + fill_rule); + } +} + +cairo_status_t +_cairo_path_fixed_fill_using_spans ( + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_path_fixed_t *path, + cairo_surface_t *dst, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + cairo_status_t status; + cairo_span_renderer_t *renderer = _cairo_surface_create_span_renderer ( + op, pattern, dst, antialias, rects); + cairo_scan_converter_t *converter = _create_scan_converter ( + fill_rule, antialias, rects); + + status = _cairo_path_fixed_fill_to_scan_converter ( + path, tolerance, converter); + if (status) + goto BAIL; + + status = converter->generate (converter, renderer); + if (status) + goto BAIL; + + status = renderer->finish (renderer); + if (status) + goto BAIL; + + BAIL: + renderer->destroy (renderer); + converter->destroy (converter); + return status; +} + +static void +_cairo_nil_destroy (void *abstract) +{ + (void) abstract; +} + +static cairo_status_t +_cairo_nil_scan_converter_add_edge (void *abstract_converter, + cairo_fixed_t x1, + cairo_fixed_t y1, + cairo_fixed_t x2, + cairo_fixed_t y2) +{ + (void) abstract_converter; + (void) x1; + (void) y1; + (void) x2; + (void) y2; + return _cairo_scan_converter_status (abstract_converter); +} + +static cairo_status_t +_cairo_nil_scan_converter_generate (void *abstract_converter, + cairo_span_renderer_t *renderer) +{ + (void) abstract_converter; + (void) renderer; + return _cairo_scan_converter_status (abstract_converter); +} + +cairo_status_t +_cairo_scan_converter_status (void *abstract_converter) +{ + cairo_scan_converter_t *converter = abstract_converter; + return converter->status; +} + +cairo_status_t +_cairo_scan_converter_set_error (void *abstract_converter, + cairo_status_t error) +{ + cairo_scan_converter_t *converter = abstract_converter; + if (error == CAIRO_STATUS_SUCCESS) + ASSERT_NOT_REACHED; + if (converter->status == CAIRO_STATUS_SUCCESS) { + converter->add_edge = _cairo_nil_scan_converter_add_edge; + converter->generate = _cairo_nil_scan_converter_generate; + converter->status = error; + } + return converter->status; +} + +static void +_cairo_nil_scan_converter_init (cairo_scan_converter_t *converter, + cairo_status_t status) +{ + converter->destroy = _cairo_nil_destroy; + converter->status = CAIRO_STATUS_SUCCESS; + status = _cairo_scan_converter_set_error (converter, status); +} + +cairo_scan_converter_t * +_cairo_scan_converter_create_in_error (cairo_status_t status) +{ +#define RETURN_NIL {\ + static cairo_scan_converter_t nil;\ + _cairo_nil_scan_converter_init (&nil, status);\ + return &nil;\ + } + switch (status) { + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + break; + case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; + case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; + case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; + case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; + case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; + case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; + case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; + case CAIRO_STATUS_READ_ERROR: RETURN_NIL; + case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; + case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; + case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; + case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; + case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; + case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; + case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; + case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; + case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; + case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; + case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; + case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; + default: + break; + } + status = CAIRO_STATUS_NO_MEMORY; + RETURN_NIL; +#undef RETURN_NIL +} + +static cairo_status_t +_cairo_nil_span_renderer_render_row ( + void *abstract_renderer, + int y, + const cairo_half_open_span_t *coverages, + unsigned num_coverages) +{ + (void) y; + (void) coverages; + (void) num_coverages; + return _cairo_span_renderer_status (abstract_renderer); +} + +static cairo_status_t +_cairo_nil_span_renderer_finish (void *abstract_renderer) +{ + return _cairo_span_renderer_status (abstract_renderer); +} + +cairo_status_t +_cairo_span_renderer_status (void *abstract_renderer) +{ + cairo_span_renderer_t *renderer = abstract_renderer; + return renderer->status; +} + +cairo_status_t +_cairo_span_renderer_set_error ( + void *abstract_renderer, + cairo_status_t error) +{ + cairo_span_renderer_t *renderer = abstract_renderer; + if (error == CAIRO_STATUS_SUCCESS) { + ASSERT_NOT_REACHED; + } + if (renderer->status == CAIRO_STATUS_SUCCESS) { + renderer->render_row = _cairo_nil_span_renderer_render_row; + renderer->finish = _cairo_nil_span_renderer_finish; + renderer->status = error; + } + return renderer->status; +} + +static void +_cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer, + cairo_status_t status) +{ + renderer->destroy = _cairo_nil_destroy; + renderer->status = CAIRO_STATUS_SUCCESS; + status = _cairo_span_renderer_set_error (renderer, status); +} + +cairo_span_renderer_t * +_cairo_span_renderer_create_in_error (cairo_status_t status) +{ +#define RETURN_NIL {\ + static cairo_span_renderer_t nil;\ + _cairo_nil_span_renderer_init (&nil, status);\ + return &nil;\ + } + switch (status) { + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + break; + case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; + case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; + case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; + case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; + case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; + case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; + case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; + case CAIRO_STATUS_READ_ERROR: RETURN_NIL; + case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; + case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; + case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; + case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; + case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; + case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; + case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; + case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; + case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; + case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; + case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; + case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; + default: + break; + } + status = CAIRO_STATUS_NO_MEMORY; + RETURN_NIL; +#undef RETURN_NIL +} diff --git a/src/cairo-spline.c b/src/cairo-spline.c index b39257e..45eedbd 100644 --- a/src/cairo-spline.c +++ b/src/cairo-spline.c @@ -36,29 +36,16 @@ #include "cairoint.h" -static cairo_status_t -_cairo_spline_grow (cairo_spline_t *spline); - -static cairo_status_t -_cairo_spline_add_point (cairo_spline_t *spline, const cairo_point_t *point); - -static void -_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result); - -static void -_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2); - -static double -_cairo_spline_error_squared (const cairo_spline_knots_t *spline); - -static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_knots_t *spline, double tolerance_squared, cairo_spline_t *result); - -cairo_int_status_t +cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, + cairo_spline_add_point_func_t add_point_func, + void *closure, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { + spline->add_point_func = add_point_func; + spline->closure = closure; + spline->knots.a = *a; spline->knots.b = *b; spline->knots.c = *c; @@ -71,7 +58,7 @@ _cairo_spline_init (cairo_spline_t *spline, else if (a->x != d->x || a->y != d->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); else - return CAIRO_INT_STATUS_DEGENERATE; + return FALSE; if (c->x != d->x || c->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); @@ -80,74 +67,20 @@ _cairo_spline_init (cairo_spline_t *spline, else _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d); - spline->points = spline->points_embedded; - spline->points_size = ARRAY_LENGTH (spline->points_embedded); - spline->num_points = 0; - - return CAIRO_STATUS_SUCCESS; + return TRUE; } -void -_cairo_spline_fini (cairo_spline_t *spline) -{ - if (spline->points != spline->points_embedded) - free (spline->points); - - spline->points = spline->points_embedded; - spline->points_size = ARRAY_LENGTH (spline->points_embedded); - spline->num_points = 0; -} - -/* make room for at least one more point */ static cairo_status_t -_cairo_spline_grow (cairo_spline_t *spline) +_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point) { - cairo_point_t *new_points; - int old_size = spline->points_size; - int new_size = 2 * MAX (old_size, 16); - - assert (spline->num_points <= spline->points_size); - - if (spline->points == spline->points_embedded) { - new_points = _cairo_malloc_ab (new_size, sizeof (cairo_point_t)); - if (new_points) - memcpy (new_points, spline->points, old_size * sizeof (cairo_point_t)); - } else { - new_points = _cairo_realloc_ab (spline->points, - new_size, sizeof (cairo_point_t)); - } - - if (new_points == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - spline->points = new_points; - spline->points_size = new_size; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_spline_add_point (cairo_spline_t *spline, const cairo_point_t *point) -{ - cairo_status_t status; cairo_point_t *prev; - if (spline->num_points) { - prev = &spline->points[spline->num_points - 1]; - if (prev->x == point->x && prev->y == point->y) - return CAIRO_STATUS_SUCCESS; - } - - if (spline->num_points >= spline->points_size) { - status = _cairo_spline_grow (spline); - if (status) - return status; - } + prev = &spline->last_point; + if (prev->x == point->x && prev->y == point->y) + return CAIRO_STATUS_SUCCESS; - spline->points[spline->num_points] = *point; - spline->num_points++; - - return CAIRO_STATUS_SUCCESS; + spline->last_point = *point; + return spline->add_point_func (spline->closure, point); } static void @@ -255,33 +188,174 @@ _cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared _de_casteljau (s1, &s2); status = _cairo_spline_decompose_into (s1, tolerance_squared, result); - if (status) - return status; - - status = _cairo_spline_decompose_into (&s2, tolerance_squared, result); - if (status) + if (unlikely (status)) return status; - return CAIRO_STATUS_SUCCESS; + return _cairo_spline_decompose_into (&s2, tolerance_squared, result); } cairo_status_t _cairo_spline_decompose (cairo_spline_t *spline, double tolerance) { - cairo_status_t status; cairo_spline_knots_t s1; - - /* reset the spline, but keep the buffer */ - spline->num_points = 0; + cairo_status_t status; s1 = spline->knots; + spline->last_point = s1.a; status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline); - if (status) + if (unlikely (status)) return status; - status = _cairo_spline_add_point (spline, &spline->knots.d); - if (status) + return _cairo_spline_add_point (spline, &spline->knots.d); +} + +/* Note: this function is only good for computing bounds in device space. */ +cairo_status_t +_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *p0, const cairo_point_t *p1, + const cairo_point_t *p2, const cairo_point_t *p3) +{ + double x0, x1, x2, x3; + double y0, y1, y2, y3; + double a, b, c; + double t[4]; + int t_num = 0, i; + cairo_status_t status; + + x0 = _cairo_fixed_to_double (p0->x); + y0 = _cairo_fixed_to_double (p0->y); + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + + /* The spline can be written as a polynomial of the four points: + * + * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3 + * + * for 0≤t≤1. Now, the X and Y components of the spline follow the + * same polynomial but with x and y replaced for p. To find the + * bounds of the spline, we just need to find the X and Y bounds. + * To find the bound, we take the derivative and equal it to zero, + * and solve to find the t's that give the extreme points. + * + * Here is the derivative of the curve, sorted on t: + * + * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1 + * + * Let: + * + * a = -p0+3p1-3p2+p3 + * b = p0-2p1+p2 + * c = -p0+p1 + * + * Gives: + * + * a.t² + 2b.t + c = 0 + * + * With: + * + * delta = b*b - a*c + * + * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if + * delta is positive, and at -b/a if delta is zero. + */ + +#define ADD(t0) \ + { \ + double _t0 = (t0); \ + if (0 < _t0 && _t0 < 1) \ + t[t_num++] = _t0; \ + } + +#define FIND_EXTREMES(a,b,c) \ + { \ + if (a == 0) { \ + if (b != 0) \ + ADD (-c / (2*b)); \ + } else { \ + double b2 = b * b; \ + double delta = b2 - a * c; \ + if (delta > 0) { \ + cairo_bool_t feasible; \ + double _2ab = 2 * a * b; \ + /* We are only interested in solutions t that satisfy 0<t<1 \ + * here. We do some checks to avoid sqrt if the solutions \ + * are not in that range. The checks can be derived from: \ + * \ + * 0 < (-b±√delta)/a < 1 \ + */ \ + if (_2ab >= 0) \ + feasible = delta > b2 && delta < a*a + b2 + _2ab; \ + else if (-b / a >= 1) \ + feasible = delta < b2 && delta > a*a + b2 + _2ab; \ + else \ + feasible = delta < b2 || delta < a*a + b2 + _2ab; \ + \ + if (unlikely (feasible)) { \ + double sqrt_delta = sqrt (delta); \ + ADD ((-b - sqrt_delta) / a); \ + ADD ((-b + sqrt_delta) / a); \ + } \ + } else if (delta == 0) { \ + ADD (-b / a); \ + } \ + } \ + } + + /* Find X extremes */ + a = -x0 + 3*x1 - 3*x2 + x3; + b = x0 - 2*x1 + x2; + c = -x0 + x1; + FIND_EXTREMES (a, b, c); + + /* Find Y extremes */ + a = -y0 + 3*y1 - 3*y2 + y3; + b = y0 - 2*y1 + y2; + c = -y0 + y1; + FIND_EXTREMES (a, b, c); + + status = add_point_func (closure, p0); + if (unlikely (status)) return status; - return CAIRO_STATUS_SUCCESS; + for (i = 0; i < t_num; i++) { + cairo_point_t p; + double x, y; + double t_1_0, t_0_1; + double t_2_0, t_0_2; + double t_3_0, t_2_1_3, t_1_2_3, t_0_3; + + t_1_0 = t[i]; /* t */ + t_0_1 = 1 - t_1_0; /* (1 - t) */ + + t_2_0 = t_1_0 * t_1_0; /* t * t */ + t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */ + + t_3_0 = t_2_0 * t_1_0; /* t * t * t */ + t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */ + t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */ + t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */ + + /* Bezier polynomial */ + x = x0 * t_0_3 + + x1 * t_1_2_3 + + x2 * t_2_1_3 + + x3 * t_3_0; + y = y0 * t_0_3 + + y1 * t_1_2_3 + + y2 * t_2_1_3 + + y3 * t_3_0; + + p.x = _cairo_fixed_from_double (x); + p.y = _cairo_fixed_from_double (y); + status = add_point_func (closure, &p); + if (unlikely (status)) + return status; + } + + return add_point_func (closure, p3); } diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c index 3bc234e..9ab91e5 100644 --- a/src/cairo-stroke-style.c +++ b/src/cairo-stroke-style.c @@ -38,6 +38,8 @@ void _cairo_stroke_style_init (cairo_stroke_style_t *style) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); + style->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; style->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; style->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; @@ -52,6 +54,11 @@ cairo_status_t _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, cairo_stroke_style_t *other) { + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); + style->line_width = other->line_width; style->line_cap = other->line_cap; style->line_join = other->line_join; @@ -63,7 +70,7 @@ _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, style->dash = NULL; } else { style->dash = _cairo_malloc_ab (style->num_dashes, sizeof (double)); - if (style->dash == NULL) + if (unlikely (style->dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (style->dash, other->dash, @@ -83,6 +90,8 @@ _cairo_stroke_style_fini (cairo_stroke_style_t *style) style->dash = NULL; } style->num_dashes = 0; + + VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t))); } /* @@ -95,9 +104,19 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, double *dx, double *dy) { - double style_expansion = MAX(style->line_cap == CAIRO_LINE_CAP_SQUARE ? M_SQRT1_2 : 0.5, - style->line_join == CAIRO_LINE_JOIN_MITER ? style->miter_limit : 0.5); + double style_expansion = 0.5; + + if (style->line_cap == CAIRO_LINE_CAP_SQUARE) + style_expansion = M_SQRT1_2; + + if (style->line_join == CAIRO_LINE_JOIN_MITER && + style_expansion < style->miter_limit) + { + style_expansion = style->miter_limit; + } + + style_expansion *= style->line_width; - *dx = style->line_width * style_expansion * (fabs(ctm->xx) + fabs(ctm->xy)); - *dy = style->line_width * style_expansion * (fabs(ctm->yy) + fabs(ctm->yx)); + *dx = style_expansion * (fabs (ctm->xx) + fabs (ctm->xy)); + *dy = style_expansion * (fabs (ctm->yy) + fabs (ctm->yx)); } diff --git a/src/cairo-supported-features.h b/src/cairo-supported-features.h deleted file mode 100644 index 93f44b8..0000000 --- a/src/cairo-supported-features.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Generated by configure. Do not edit. */ -#ifndef CAIRO_SUPPORTED_FEATURES_H -#define CAIRO_SUPPORTED_FEATURES_H - -/* This is a dummy header, to trick gtk-doc only */ - -#define CAIRO_HAS_XLIB_SURFACE 1 -#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 -#define CAIRO_HAS_QUARTZ_SURFACE 1 -#define CAIRO_HAS_QUARTZ_FONT 1 -#define CAIRO_HAS_WIN32_SURFACE 1 -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_PNG_FUNCTIONS 1 -#define CAIRO_HAS_FT_FONT 1 -#define CAIRO_HAS_PS_SURFACE 1 -#define CAIRO_HAS_PDF_SURFACE 1 -#define CAIRO_HAS_SVG_SURFACE 1 -#define CAIRO_HAS_IMAGE_SURFACE 1 -#define CAIRO_HAS_USER_FONT 1 - -#endif diff --git a/src/cairo-surface-fallback-private.h b/src/cairo-surface-fallback-private.h index 2e10241..cd18178 100644 --- a/src/cairo-surface-fallback-private.h +++ b/src/cairo-surface-fallback-private.h @@ -42,19 +42,20 @@ #include "cairoint.h" cairo_private cairo_status_t -_cairo_surface_fallback_paint (cairo_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source); +_cairo_surface_fallback_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source); + cairo_private cairo_status_t _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask); + const cairo_pattern_t *source, + const cairo_pattern_t *mask); cairo_private cairo_status_t _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, cairo_matrix_t *ctm, @@ -65,16 +66,16 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, - double tolerance, + double tolerance, cairo_antialias_t antialias); cairo_private cairo_status_t _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font); @@ -83,18 +84,18 @@ cairo_private cairo_surface_t * _cairo_surface_fallback_snapshot (cairo_surface_t *surface); cairo_private cairo_status_t -_cairo_surface_fallback_composite (cairo_operator_t op, - cairo_pattern_t *src, - cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height); +_cairo_surface_fallback_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height); cairo_private cairo_status_t _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, @@ -105,7 +106,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *dst, cairo_antialias_t antialias, int src_x, @@ -120,6 +121,7 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, cairo_private cairo_status_t _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c index 5b9c99a..830c1b3 100644 --- a/src/cairo-surface-fallback.c +++ b/src/cairo-surface-fallback.c @@ -79,7 +79,7 @@ _fallback_init (fallback_state_t *state, status = _cairo_surface_acquire_dest_image (dst, &state->extents, &state->image, &state->image_rect, &state->image_extra); - if (status) + if (unlikely (status)) return status; /* XXX: This NULL value tucked away in state->image is a rather @@ -103,7 +103,7 @@ _fallback_fini (fallback_state_t *state) typedef cairo_status_t (*cairo_draw_func_t) (void *closure, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_surface_t *dst, int dst_x, int dst_y, @@ -131,7 +131,7 @@ _create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, NULL, mask, extents->x, extents->y, extents); - if (status) + if (unlikely (status)) goto CLEANUP_SURFACE; if (clip && clip->surface) @@ -139,7 +139,7 @@ _create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, mask, extents->x, extents->y, extents); - if (status) + if (unlikely (status)) goto CLEANUP_SURFACE; _cairo_pattern_init_for_surface (mask_pattern, mask); @@ -156,7 +156,7 @@ _create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, static cairo_status_t _clip_and_composite_with_mask (cairo_clip_t *clip, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_draw_func_t draw_func, void *draw_closure, cairo_surface_t *dst, @@ -169,7 +169,7 @@ _clip_and_composite_with_mask (cairo_clip_t *clip, clip, draw_func, draw_closure, dst, extents); - if (status) + if (unlikely (status)) return status; status = _cairo_surface_composite (op, @@ -190,7 +190,7 @@ _clip_and_composite_with_mask (cairo_clip_t *clip, static cairo_status_t _clip_and_composite_combine (cairo_clip_t *clip, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_draw_func_t draw_func, void *draw_closure, cairo_surface_t *dst, @@ -226,14 +226,14 @@ _clip_and_composite_combine (cairo_clip_t *clip, _cairo_pattern_fini (&dst_pattern.base); - if (status) + if (unlikely (status)) goto CLEANUP_SURFACE; status = (*draw_func) (draw_closure, op, src, intermediate, extents->x, extents->y, extents); - if (status) + if (unlikely (status)) goto CLEANUP_SURFACE; /* Combine that with the clip @@ -242,7 +242,7 @@ _clip_and_composite_combine (cairo_clip_t *clip, intermediate, extents->x, extents->y, extents); - if (status) + if (unlikely (status)) goto CLEANUP_SURFACE; /* Punch the clip out of the destination @@ -251,7 +251,7 @@ _clip_and_composite_combine (cairo_clip_t *clip, dst, 0, 0, extents); - if (status) + if (unlikely (status)) goto CLEANUP_SURFACE; /* Now add the two results together @@ -278,7 +278,7 @@ _clip_and_composite_combine (cairo_clip_t *clip, */ static cairo_status_t _clip_and_composite_source (cairo_clip_t *clip, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_draw_func_t draw_func, void *draw_closure, cairo_surface_t *dst, @@ -293,7 +293,7 @@ _clip_and_composite_source (cairo_clip_t *clip, clip, draw_func, draw_closure, dst, extents); - if (status) + if (unlikely (status)) return status; /* Compute dest' = dest OUT (mask IN clip) @@ -305,7 +305,7 @@ _clip_and_composite_source (cairo_clip_t *clip, extents->x, extents->y, extents->width, extents->height); - if (status) + if (unlikely (status)) goto CLEANUP_MASK_PATTERN; /* Now compute (src IN (mask IN clip)) ADD dest' @@ -352,7 +352,7 @@ _cairo_rectangle_empty (const cairo_rectangle_int_t *rect) static cairo_status_t _clip_and_composite (cairo_clip_t *clip, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_draw_func_t draw_func, void *draw_closure, cairo_surface_t *dst, @@ -398,9 +398,6 @@ _clip_and_composite (cairo_clip_t *clip, extents); } - if (src == &solid_pattern.base) - _cairo_pattern_fini (&solid_pattern.base); - return status; } @@ -408,7 +405,7 @@ _clip_and_composite (cairo_clip_t *clip, */ static cairo_status_t _composite_trap_region (cairo_clip_t *clip, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_operator_t op, cairo_surface_t *dst, cairo_region_t *trap_region, @@ -417,10 +414,13 @@ _composite_trap_region (cairo_clip_t *clip, cairo_status_t status; cairo_solid_pattern_t solid_pattern; cairo_surface_pattern_t mask; - int num_rects = _cairo_region_num_boxes (trap_region); + int num_rects = cairo_region_num_rectangles (trap_region); unsigned int clip_serial; cairo_surface_t *clip_surface = clip ? clip->surface : NULL; + if (num_rects == 0) + return CAIRO_STATUS_SUCCESS; + if (clip_surface && op == CAIRO_OPERATOR_CLEAR) { _cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE, CAIRO_CONTENT_COLOR); @@ -428,9 +428,6 @@ _composite_trap_region (cairo_clip_t *clip, op = CAIRO_OPERATOR_DEST_OUT; } - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; - if (num_rects > 1) { if (_cairo_surface_get_clip_mode (dst) != CAIRO_CLIP_MODE_REGION) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -439,7 +436,7 @@ _composite_trap_region (cairo_clip_t *clip, status = _cairo_surface_set_clip_region (dst, trap_region, clip_serial); - if (status) + if (unlikely (status)) return status; } @@ -466,9 +463,6 @@ _composite_trap_region (cairo_clip_t *clip, if (clip_surface) _cairo_pattern_fini (&mask.base); - if (src == &solid_pattern.base) - _cairo_pattern_fini (&solid_pattern.base); - return status; } @@ -480,7 +474,7 @@ typedef struct { static cairo_status_t _composite_traps_draw_func (void *closure, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_surface_t *dst, int dst_x, int dst_y, @@ -488,31 +482,28 @@ _composite_traps_draw_func (void *closure, { cairo_composite_traps_info_t *info = closure; cairo_solid_pattern_t pattern; - cairo_status_t status; if (dst_x != 0 || dst_y != 0) _cairo_traps_translate (info->traps, - dst_x, - dst_y); - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - if (!src) + if (src == NULL) { + _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); src = &pattern.base; + } - status = _cairo_surface_composite_trapezoids (op, - src, dst, info->antialias, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - info->traps->traps, - info->traps->num_traps); - _cairo_pattern_fini (&pattern.base); - - return status; + return _cairo_surface_composite_trapezoids (op, + src, dst, info->antialias, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + info->traps->traps, + info->traps->num_traps); } /* Warning: This call modifies the coordinates of traps */ static cairo_status_t -_clip_and_composite_trapezoids (cairo_pattern_t *src, +_clip_and_composite_trapezoids (const cairo_pattern_t *src, cairo_operator_t op, cairo_surface_t *dst, cairo_traps_t *traps, @@ -520,10 +511,8 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, cairo_antialias_t antialias) { cairo_status_t status; - cairo_region_t trap_region; - cairo_region_t clear_region; - cairo_bool_t has_trap_region = FALSE; - cairo_bool_t has_clear_region = FALSE; + cairo_region_t *trap_region = NULL; + cairo_region_t *clear_region = NULL; cairo_rectangle_int_t extents; cairo_composite_traps_info_t traps_info; @@ -531,27 +520,22 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, return CAIRO_STATUS_SUCCESS; status = _cairo_surface_get_extents (dst, &extents); - if (status) + if (unlikely (status)) return status; status = _cairo_traps_extract_region (traps, &trap_region); - if (CAIRO_INT_STATUS_UNSUPPORTED == status) { - has_trap_region = FALSE; - } else if (status) { - return status; - } else { - has_trap_region = TRUE; - } + if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t trap_extents; - if (has_trap_region) { - status = _cairo_clip_intersect_to_region (clip, &trap_region); - if (status) + if (trap_region) { + status = _cairo_clip_intersect_to_region (clip, trap_region); + if (unlikely (status)) goto out; - _cairo_region_get_extents (&trap_region, &trap_extents); + cairo_region_get_extents (trap_region, &trap_extents); } else { cairo_box_t trap_box; _cairo_traps_extents (traps, &trap_box); @@ -564,43 +548,45 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, } status = _cairo_clip_intersect_to_rectangle (clip, &extents); - if (status) + if (unlikely (status)) goto out; } else { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; - if (has_trap_region && !clip_surface) { + if (trap_region && !clip_surface) { /* If we optimize drawing with an unbounded operator to * _cairo_surface_fill_rectangles() or to drawing with a * clip region, then we have an additional region to clear. */ - _cairo_region_init_rect (&clear_region, &extents); + clear_region = cairo_region_create_rectangle (&extents); - has_clear_region = TRUE; - status = _cairo_clip_intersect_to_region (clip, &clear_region); + status = cairo_region_status (clear_region); + if (unlikely (status)) + goto out; - if (status) + status = _cairo_clip_intersect_to_region (clip, clear_region); + if (unlikely (status)) goto out; - _cairo_region_get_extents (&clear_region, &extents); + cairo_region_get_extents (clear_region, &extents); - status = _cairo_region_subtract (&clear_region, &clear_region, &trap_region); - if (status) + status = cairo_region_subtract (clear_region, trap_region); + if (unlikely (status)) goto out; - if (!_cairo_region_not_empty (&clear_region)) { - _cairo_region_fini (&clear_region); - has_clear_region = FALSE; + if (cairo_region_is_empty (clear_region)) { + cairo_region_destroy (clear_region); + clear_region = NULL; } } else { status = _cairo_clip_intersect_to_rectangle (clip, &extents); } } - if (status) + if (unlikely (status)) goto out; - if (has_trap_region) { + if (trap_region) { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; if ((src->type == CAIRO_PATTERN_TYPE_SOLID || @@ -614,12 +600,13 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, } /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, &trap_region); + status = _cairo_surface_fill_region (dst, op, color, trap_region); - if (!status && has_clear_region) + if (!status && clear_region) { status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, - &clear_region); + clear_region); + } goto out; } @@ -634,7 +621,7 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, * If we have a clip surface, we set it as the mask; this only works * for bounded operators other than SOURCE; for unbounded operators, * clip and mask cannot be interchanged. For SOURCE, the operator - * as implemented by the backends is different in it's handling + * as implemented by the backends is different in its handling * of the mask then what we want. * * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has @@ -642,13 +629,13 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, * regions. In that case, we fall through. */ status = _composite_trap_region (clip, src, op, dst, - &trap_region, &extents); + trap_region, &extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - if (!status && has_clear_region) + if (!status && clear_region) status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, - &clear_region); + clear_region); goto out; } } @@ -662,18 +649,62 @@ _clip_and_composite_trapezoids (cairo_pattern_t *src, &traps_info, dst, &extents); out: - if (has_trap_region) - _cairo_region_fini (&trap_region); - if (has_clear_region) - _cairo_region_fini (&clear_region); + if (trap_region) + cairo_region_destroy (trap_region); + if (clear_region) + cairo_region_destroy (clear_region); return status; } +typedef struct { + cairo_path_fixed_t *path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; +} cairo_composite_spans_fill_info_t; + +static cairo_status_t +_composite_spans_fill_func (void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_surface_t *dst, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents) +{ + cairo_composite_rectangles_t rects; + cairo_composite_spans_fill_info_t *info = closure; + cairo_solid_pattern_t pattern; + + _cairo_composite_rectangles_init ( + &rects, extents->x, extents->y, + extents->width, extents->height); + + /* The incoming dst_x/y are where we're pretending the origin of + * the dst surface is -- *not* the offset of a rectangle where + * we'd like to place the result. */ + rects.dst.x -= dst_x; + rects.dst.y -= dst_y; + + /* We're called without a source pattern from + * _create_composite_mask_pattern(). */ + if (src == NULL) { + _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); + src = &pattern.base; + } + + return _cairo_path_fixed_fill_using_spans ( + op, src, info->path, dst, + info->fill_rule, info->tolerance, info->antialias, + &rects); +} + cairo_status_t -_cairo_surface_fallback_paint (cairo_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source) +_cairo_surface_fallback_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) { cairo_status_t status; cairo_rectangle_int_t extents; @@ -681,13 +712,14 @@ _cairo_surface_fallback_paint (cairo_surface_t *surface, cairo_traps_t traps; status = _cairo_surface_get_extents (surface, &extents); - if (status) + if (unlikely (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; + status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &source_extents)) @@ -695,7 +727,7 @@ _cairo_surface_fallback_paint (cairo_surface_t *surface, } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (status) + if (unlikely (status)) return status; _cairo_box_from_rectangle (&box, &extents); @@ -717,7 +749,7 @@ _cairo_surface_fallback_paint (cairo_surface_t *surface, static cairo_status_t _cairo_surface_mask_draw_func (void *closure, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_surface_t *dst, int dst_x, int dst_y, @@ -744,19 +776,19 @@ _cairo_surface_mask_draw_func (void *closure, cairo_status_t _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask) { cairo_status_t status; cairo_rectangle_int_t extents, source_extents, mask_extents; status = _cairo_surface_get_extents (surface, &extents); - if (status) + if (unlikely (status)) return status; if (_cairo_operator_bounded_by_source (op)) { status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &source_extents)) @@ -765,7 +797,7 @@ _cairo_surface_fallback_mask (cairo_surface_t *surface, if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_pattern_get_extents (mask, &mask_extents); - if (status) + if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &mask_extents)) @@ -773,13 +805,13 @@ _cairo_surface_fallback_mask (cairo_surface_t *surface, } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (status) + if (unlikely (status)) return status; status = _clip_and_composite (surface->clip, op, source, _cairo_surface_mask_draw_func, - mask, + (void *) mask, surface, &extents); @@ -789,7 +821,7 @@ _cairo_surface_fallback_mask (cairo_surface_t *surface, cairo_status_t _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, cairo_matrix_t *ctm, @@ -803,13 +835,13 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, cairo_rectangle_int_t extents; status = _cairo_surface_get_extents (surface, &extents); - if (status) + if (unlikely (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &source_extents)) @@ -817,7 +849,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (status) + if (unlikely (status)) return status; if (extents.width == 0 || extents.height == 0) @@ -833,7 +865,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface, ctm, ctm_inverse, tolerance, &traps); - if (status) + if (unlikely (status)) goto FAIL; status = _clip_and_composite_trapezoids (source, @@ -852,7 +884,7 @@ FAIL: cairo_status_t _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, @@ -864,14 +896,14 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, cairo_rectangle_int_t extents; status = _cairo_surface_get_extents (surface, &extents); - if (status) + if (unlikely (status)) return status; if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; status = _cairo_pattern_get_extents (source, &source_extents); - if (status) + if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &source_extents)) @@ -879,14 +911,53 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (status) + if (unlikely (status)) return status; if (extents.width == 0 || extents.height == 0) return CAIRO_STATUS_SUCCESS; - _cairo_box_from_rectangle (&box, &extents); + /* Ask if the surface would like to render this combination of + * op/source/dst/antialias with spans or not, but don't actually + * make a renderer yet. We'll try to hit the region optimisations + * in _clip_and_composite_trapezoids() if it looks like the path + * is a region. */ + /* TODO: Until we have a mono scan converter we won't even try + * to use spans for CAIRO_ANTIALIAS_NONE. */ + /* TODO: The region filling code should be lifted from + * _clip_and_composite_trapezoids() and given first priority + * explicitly before deciding between spans and trapezoids. */ + if (antialias != CAIRO_ANTIALIAS_NONE && + !_cairo_path_fixed_is_box (path, &box) && + !_cairo_path_fixed_is_region (path) && + _cairo_surface_check_span_renderer ( + op, source, surface, antialias, NULL)) + { + cairo_composite_spans_fill_info_t info; + info.path = path; + info.fill_rule = fill_rule; + info.tolerance = tolerance; + info.antialias = antialias; + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t path_extents; + + _cairo_path_fixed_approximate_clip_extents (path, + &path_extents); + if (! _cairo_rectangle_intersect (&extents, &path_extents)) + return CAIRO_STATUS_SUCCESS; + } + + return _clip_and_composite ( + surface->clip, op, source, + _composite_spans_fill_func, + &info, + surface, + &extents); + } + /* Fall back to trapezoid fills. */ + _cairo_box_from_rectangle (&box, &extents); _cairo_traps_init (&traps); _cairo_traps_limit (&traps, &box); @@ -894,7 +965,7 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface, fill_rule, tolerance, &traps); - if (status) { + if (unlikely (status)) { _cairo_traps_fini (&traps); return status; } @@ -920,7 +991,7 @@ typedef struct { static cairo_status_t _cairo_surface_old_show_glyphs_draw_func (void *closure, cairo_operator_t op, - cairo_pattern_t *src, + const cairo_pattern_t *src, cairo_surface_t *dst, int dst_x, int dst_y, @@ -944,10 +1015,11 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure } } - _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, - CAIRO_CONTENT_COLOR); - if (!src) + if (src == NULL) { + _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE, + CAIRO_CONTENT_COLOR); src = &pattern.base; + } status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, dst, @@ -958,30 +1030,24 @@ _cairo_surface_old_show_glyphs_draw_func (void *closure extents->height, glyph_info->glyphs, glyph_info->num_glyphs); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - status = _cairo_scaled_font_show_glyphs (glyph_info->font, - op, - src, dst, - extents->x, extents->y, - extents->x - dst_x, - extents->y - dst_y, - extents->width, extents->height, - glyph_info->glyphs, - glyph_info->num_glyphs); - - if (src == &pattern.base) - _cairo_pattern_fini (&pattern.base); - - return status; + return _cairo_scaled_font_show_glyphs (glyph_info->font, + op, + src, dst, + extents->x, extents->y, + extents->x - dst_x, + extents->y - dst_y, + extents->width, extents->height, + glyph_info->glyphs, + glyph_info->num_glyphs); } cairo_status_t _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font) @@ -991,7 +1057,7 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, cairo_show_glyphs_info_t glyph_info; status = _cairo_surface_get_extents (surface, &extents); - if (status) + if (unlikely (status)) return status; if (_cairo_operator_bounded_by_mask (op)) { @@ -1001,7 +1067,7 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, glyphs, num_glyphs, &glyph_extents); - if (status) + if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&extents, &glyph_extents)) @@ -1009,7 +1075,7 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, } status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents); - if (status) + if (unlikely (status)) return status; glyph_info.font = scaled_font; @@ -1032,26 +1098,32 @@ _cairo_surface_fallback_snapshot (cairo_surface_t *surface) { cairo_surface_t *snapshot; cairo_status_t status; + cairo_format_t format; cairo_surface_pattern_t pattern; cairo_image_surface_t *image; void *image_extra; status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); - if (status) + if (unlikely (status)) return _cairo_surface_create_in_error (status); - snapshot = cairo_image_surface_create (image->format, + format = image->format; + if (format == CAIRO_FORMAT_INVALID) { + /* Non-standard images formats can be generated when retrieving + * images from unusual xservers, for example. + */ + format = _cairo_format_from_content (image->base.content); + } + snapshot = cairo_image_surface_create (format, image->width, image->height); if (cairo_surface_status (snapshot)) { - _cairo_surface_release_source_image (surface, - image, image_extra); + _cairo_surface_release_source_image (surface, image, image_extra); return snapshot; } _cairo_pattern_init_for_surface (&pattern, &image->base); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, &pattern.base, NULL, @@ -1061,43 +1133,35 @@ _cairo_surface_fallback_snapshot (cairo_surface_t *surface) 0, 0, image->width, image->height); - _cairo_pattern_fini (&pattern.base); - _cairo_surface_release_source_image (surface, - image, image_extra); - - if (status) { + _cairo_surface_release_source_image (surface, image, image_extra); + if (unlikely (status)) { cairo_surface_destroy (snapshot); return _cairo_surface_create_in_error (status); } - snapshot->device_transform = surface->device_transform; - snapshot->device_transform_inverse = surface->device_transform_inverse; - - snapshot->is_snapshot = TRUE; - return snapshot; } cairo_status_t -_cairo_surface_fallback_composite (cairo_operator_t op, - cairo_pattern_t *src, - cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) +_cairo_surface_fallback_composite (cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { fallback_state_t state; cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (status) { + if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; return status; @@ -1132,7 +1196,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, int x1, y1, x2, y2; int i; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (num_rects <= 0) return CAIRO_STATUS_SUCCESS; @@ -1158,7 +1222,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, } status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); - if (status) { + if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; return status; @@ -1168,7 +1232,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, if (state.image_rect.x != 0 || state.image_rect.y != 0) { offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); - if (offset_rects == NULL) { + if (unlikely (offset_rects == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto DONE; } @@ -1197,7 +1261,7 @@ _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, cairo_status_t _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *dst, cairo_antialias_t antialias, int src_x, @@ -1214,7 +1278,7 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, cairo_status_t status; status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (status) { + if (unlikely (status)) { if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; return status; @@ -1255,6 +1319,7 @@ _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, cairo_status_t _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -1263,12 +1328,12 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, int *clone_offset_y, cairo_surface_t **clone_out) { + cairo_surface_t *new_surface; + cairo_surface_pattern_t pattern; cairo_status_t status; - cairo_surface_t *new_surface = NULL; - cairo_t *cr; new_surface = _cairo_surface_create_similar_scratch (surface, - cairo_surface_get_content (src), + src->content & content, width, height); if (new_surface->status) return new_surface->status; @@ -1277,25 +1342,22 @@ _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, new_surface->device_transform = src->device_transform; new_surface->device_transform_inverse = src->device_transform_inverse; - /* We can't use _cairo_composite directly, because backends that - * implement the "high-level" API may not have it implemented. - * (For example, SVG.) We can fix this by either checking if the - * destination supports composite first, or we can make clone a - * required "high-level" operation. - */ - cr = cairo_create (new_surface); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface (cr, src, -src_x, -src_y); - cairo_paint (cr); - status = cairo_status (cr); - cairo_destroy (cr); - - if (status == CAIRO_STATUS_SUCCESS) { - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = new_surface; - } else + _cairo_pattern_init_for_surface (&pattern, src); + cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + + status = _cairo_surface_paint (new_surface, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { cairo_surface_destroy (new_surface); + return status; + } - return status; + *clone_offset_x = src_x; + *clone_offset_y = src_y; + *clone_out = new_surface; + return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h index efd4365..c25b6dc 100644 --- a/src/cairo-surface-private.h +++ b/src/cairo-surface-private.h @@ -43,6 +43,8 @@ #include "cairo-types-private.h" #include "cairo-reference-count-private.h" +typedef void (*cairo_surface_func_t) (cairo_surface_t *); + struct _cairo_surface { const cairo_surface_backend_t *backend; @@ -56,7 +58,10 @@ struct _cairo_surface { cairo_reference_count_t ref_count; cairo_status_t status; cairo_bool_t finished; + unsigned int unique_id; + cairo_user_data_array_t user_data; + cairo_user_data_array_t mime_data; cairo_matrix_t device_transform; cairo_matrix_t device_transform_inverse; @@ -91,7 +96,10 @@ struct _cairo_surface { unsigned int current_clip_serial; /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ - cairo_bool_t is_snapshot; + cairo_surface_t *snapshot_of; + cairo_surface_func_t snapshot_detach; + /* current snapshots of this surface */ + cairo_array_t snapshots; /* * Surface font options, falling back to backend's default options, diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 609a0e0..fdb25fa 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -40,28 +40,21 @@ #include "cairo-surface-fallback-private.h" #include "cairo-clip-private.h" +#include "cairo-meta-surface-private.h" #define DEFINE_NIL_SURFACE(status, name) \ const cairo_surface_t name = { \ - &_cairo_image_surface_backend, /* backend */ \ - CAIRO_SURFACE_TYPE_IMAGE, \ - CAIRO_CONTENT_COLOR, \ + NULL, /* backend */ \ + CAIRO_SURFACE_TYPE_IMAGE, /* type */ \ + CAIRO_CONTENT_COLOR, /* content */ \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ FALSE, /* finished */ \ - { 0, /* size */ \ - 0, /* num_elements */ \ - 0, /* element_size */ \ - NULL, /* elements */ \ - }, /* user_data */ \ - { 1.0, 0.0, \ - 0.0, 1.0, \ - 0.0, 0.0 \ - }, /* device_transform */ \ - { 1.0, 0.0, \ - 0.0, 1.0, \ - 0.0, 0.0 \ - }, /* device_transform_inverse */ \ + 0, /* unique id */ \ + { 0, 0, 0, NULL, }, /* user_data */ \ + { 0, 0, 0, NULL, }, /* mime_data */ \ + { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ + { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \ 0.0, /* x_resolution */ \ 0.0, /* y_resolution */ \ 0.0, /* x_fallback_resolution */ \ @@ -69,7 +62,13 @@ const cairo_surface_t name = { \ NULL, /* clip */ \ 0, /* next_clip_serial */ \ 0, /* current_clip_serial */ \ - FALSE, /* is_snapshot */ \ + NULL, /* snapshot_of */ \ + NULL, /* snapshot_detach */ \ + { 0, /* size */ \ + 0, /* num_elements */ \ + 0, /* element_size */ \ + NULL, /* elements */ \ + }, /* snapshots */ \ FALSE, /* has_font_options */ \ { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ @@ -87,11 +86,12 @@ static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_ static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error); static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size); static cairo_status_t -_cairo_surface_copy_pattern_for_destination (const cairo_pattern_t *pattern, +_cairo_surface_copy_pattern_for_destination (const cairo_pattern_t **pattern, cairo_surface_t *destination, - cairo_pattern_t **pattern_out); + cairo_pattern_t *pattern_copy); /** * _cairo_surface_set_error: @@ -187,6 +187,136 @@ cairo_surface_status (cairo_surface_t *surface) } slim_hidden_def (cairo_surface_status); +static unsigned int +_cairo_surface_allocate_unique_id (void) +{ + static unsigned int unique_id; + +#if CAIRO_NO_MUTEX + if (++unique_id == 0) + unique_id = 1; + return unique_id; +#else + unsigned int old, id; + + do { + old = _cairo_atomic_uint_get (&unique_id); + id = old + 1; + if (id == 0) + id = 1; + } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id)); + + return id; +#endif +} + +static cairo_bool_t +_cairo_surface_has_snapshots (cairo_surface_t *surface) +{ + return surface->snapshots.num_elements != 0; +} + +static void +_cairo_surface_detach_snapshots (cairo_surface_t *surface) +{ + cairo_surface_t **snapshots; + unsigned int i; + + if (! _cairo_surface_has_snapshots (surface)) + return; + + /* XXX do something intelligent! */ + + snapshots = _cairo_array_index (&surface->snapshots, 0); + for (i = 0; i < surface->snapshots.num_elements; i++) { + snapshots[i]->snapshot_of = NULL; + + if (snapshots[i]->snapshot_detach != NULL) + snapshots[i]->snapshot_detach (snapshots[i]); + } + surface->snapshots.num_elements = 0; + + assert (! _cairo_surface_has_snapshots (surface)); +} + +cairo_status_t +_cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func) +{ + assert (surface != snapshot); + + if (snapshot->snapshot_of != NULL) + _cairo_surface_detach_snapshot (snapshot); + + snapshot->snapshot_of = surface; + snapshot->snapshot_detach = detach_func; + + return _cairo_array_append (&surface->snapshots, &snapshot); +} + +cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend) +{ + cairo_surface_t **snapshots; + unsigned int i; + + snapshots = _cairo_array_index (&surface->snapshots, 0); + for (i = 0; i < surface->snapshots.num_elements; i++) { + if (snapshots[i]->backend == backend) + return snapshots[i]; + } + + return NULL; +} + +void +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot) +{ + cairo_surface_t *surface; + cairo_surface_t **snapshots; + unsigned int i; + + assert (snapshot->snapshot_of != NULL); + surface = snapshot->snapshot_of; + + snapshots = _cairo_array_index (&surface->snapshots, 0); + for (i = 0; i < surface->snapshots.num_elements; i++) { + if (snapshots[i] == snapshot) + break; + } + assert (i < surface->snapshots.num_elements); + + surface->snapshots.num_elements--; + memmove (&snapshots[i], + &snapshots[i+1], + sizeof (cairo_surface_t *)*(surface->snapshots.num_elements - i)); + + snapshot->snapshot_of = NULL; + + if (snapshot->snapshot_detach != NULL) + snapshot->snapshot_detach (snapshot); +} + +static cairo_bool_t +_cairo_surface_is_writable (cairo_surface_t *surface) +{ + return ! surface->finished && + surface->snapshot_of == NULL && + ! _cairo_surface_has_snapshots (surface); +} + +static void +_cairo_surface_begin_modification (cairo_surface_t *surface) +{ + assert (surface->status == CAIRO_STATUS_SUCCESS); + assert (! surface->finished); + assert (surface->snapshot_of == NULL); + + _cairo_surface_detach_snapshots (surface); +} + void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend, @@ -201,8 +331,10 @@ _cairo_surface_init (cairo_surface_t *surface, CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); surface->status = CAIRO_STATUS_SUCCESS; surface->finished = FALSE; + surface->unique_id = _cairo_surface_allocate_unique_id (); _cairo_user_data_array_init (&surface->user_data); + _cairo_user_data_array_init (&surface->mime_data); cairo_matrix_init_identity (&surface->device_transform); cairo_matrix_init_identity (&surface->device_transform_inverse); @@ -217,7 +349,8 @@ _cairo_surface_init (cairo_surface_t *surface, surface->next_clip_serial = 0; surface->current_clip_serial = 0; - surface->is_snapshot = FALSE; + _cairo_array_init (&surface->snapshots, sizeof (cairo_surface_t *)); + surface->snapshot_of = NULL; surface->has_font_options = FALSE; } @@ -229,9 +362,6 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, int height) { cairo_surface_t *surface = NULL; - cairo_font_options_t options; - - cairo_format_t format = _cairo_format_from_content (content); if (other->status) return _cairo_surface_create_in_error (other->status); @@ -242,15 +372,22 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, return surface; } - if (surface == NULL) - surface = cairo_image_surface_create (format, width, height); + if (surface == NULL) { + surface = + cairo_image_surface_create (_cairo_format_from_content (content), + width, height); + } /* If any error occurred, then return the nil surface we received. */ - if (surface->status) + if (unlikely (surface->status)) return surface; - cairo_surface_get_font_options (other, &options); - _cairo_surface_set_font_options (surface, &options); + if (other->has_font_options || other->backend != surface->backend) { + cairo_font_options_t options; + + cairo_surface_get_font_options (other, &options); + _cairo_surface_set_font_options (surface, &options); + } cairo_surface_set_fallback_resolution (surface, other->x_fallback_resolution, @@ -323,11 +460,11 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, status = _cairo_surface_paint (surface, color == CAIRO_COLOR_TRANSPARENT ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, - &solid_pattern.base); + &solid_pattern.base, NULL); _cairo_pattern_fini (&solid_pattern.base); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (surface); return _cairo_surface_create_in_error (status); } @@ -337,39 +474,46 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, cairo_surface_t * _cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, - cairo_solid_pattern_t *solid_pattern) + const cairo_solid_pattern_t *solid_pattern) { - cairo_surface_t *surface; + if (other->backend->create_solid_pattern_surface != NULL) { + cairo_surface_t *surface; - if (other->backend->create_solid_pattern_surface) { - surface = other->backend->create_solid_pattern_surface (other, solid_pattern); + surface = other->backend->create_solid_pattern_surface (other, + solid_pattern); if (surface) return surface; } - surface = _cairo_surface_create_similar_solid (other, - solid_pattern->content, - 1, 1, - &solid_pattern->color); - return surface; + return _cairo_surface_create_similar_solid (other, + solid_pattern->content, + 1, 1, + &solid_pattern->color); } cairo_int_status_t _cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, cairo_surface_t *solid_surface, - cairo_solid_pattern_t *solid_pattern) -{ - if (other->backend->create_solid_pattern_surface) - /* Solid pattern surface for this backend are not trivial to make. - * Skip repainting. - * - * This does not work optimally with things like analysis surface that - * are proxies. But returning UNSUPPORTED is *safe* as it only - * disables some caching. - */ + const cairo_solid_pattern_t *solid_pattern) +{ + /* Solid pattern surface for these backends are special and not trivial + * to repaint. Skip repainting. + * + * This does not work optimally with things like analysis surface that + * are proxies. But returning UNSUPPORTED is *safe* as it only + * disables some caching. + */ + if (other->backend->create_solid_pattern_surface != NULL && + ! other->backend->can_repaint_solid_pattern_surface (solid_surface, + solid_pattern)) + { return CAIRO_INT_STATUS_UNSUPPORTED; + } - return _cairo_surface_paint (solid_surface, CAIRO_OPERATOR_SOURCE, &solid_pattern->base); + return _cairo_surface_paint (solid_surface, + CAIRO_OPERATOR_SOURCE, + &solid_pattern->base, + NULL); } cairo_clip_mode_t @@ -435,6 +579,8 @@ cairo_surface_destroy (cairo_surface_t *surface) cairo_surface_finish (surface); _cairo_user_data_array_fini (&surface->user_data); + _cairo_user_data_array_fini (&surface->mime_data); + _cairo_array_fini (&surface->snapshots); free (surface); } @@ -457,10 +603,11 @@ _cairo_surface_reset (cairo_surface_t *surface) assert (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) == 1); _cairo_user_data_array_fini (&surface->user_data); + _cairo_user_data_array_fini (&surface->mime_data); if (surface->backend->reset != NULL) { cairo_status_t status = surface->backend->reset (surface); - if (status) + if (unlikely (status)) return _cairo_surface_set_error (surface, status); } @@ -528,11 +675,14 @@ cairo_surface_finish (cairo_surface_t *surface) /* call finish even if in error mode */ if (surface->backend->finish) { status = surface->backend->finish (surface); - if (status) + if (unlikely (status)) status = _cairo_surface_set_error (surface, status); } surface->finished = TRUE; + + if (surface->snapshot_of != NULL) + _cairo_surface_detach_snapshot (surface); } slim_hidden_def (cairo_surface_finish); @@ -586,6 +736,166 @@ cairo_surface_set_user_data (cairo_surface_t *surface, } /** + * cairo_surface_get_mime_data: + * @surface: a #cairo_surface_t + * @mime_type: the mime type of the image data + * @data: the image data to attached to the surface + * @length: the length of the image data + * + * Return mime data previously attached to @surface using the + * specified mime type. If no data has been attached with the given + * mime type, @data is set %NULL. + * + * Since: 1.10 + **/ +void +cairo_surface_get_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char **data, + unsigned int *length) +{ + cairo_user_data_slot_t *slots; + int i, num_slots; + + *data = NULL; + *length = 0; + if (unlikely (surface->status)) + return; + + /* The number of mime-types attached to a surface is usually small, + * typically zero. Therefore it is quicker to do a strcmp() against + * each key than it is to intern the string (i.e. compute a hash, + * search the hash table, and do a final strcmp). + */ + num_slots = surface->mime_data.num_elements; + slots = _cairo_array_index (&surface->mime_data, 0); + for (i = 0; i < num_slots; i++) { + if (strcmp ((char *) slots[i].key, mime_type) == 0) { + cairo_mime_data_t *mime_data = slots[i].user_data; + + *data = mime_data->data; + *length = mime_data->length; + return; + } + } +} +slim_hidden_def (cairo_surface_get_mime_data); + +static void +_cairo_mime_data_destroy (void *ptr) +{ + cairo_mime_data_t *mime_data = ptr; + + if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count)) + return; + + if (mime_data->destroy && mime_data->closure) + mime_data->destroy (mime_data->closure); + + free (mime_data); +} + +/** + * cairo_surface_set_mime_data: + * @surface: a #cairo_surface_t + * @mime_type: the mime type of the image data + * @data: the image data to attach to the surface + * @length: the length of the image data + * @destroy: a #cairo_destroy_func_t which will be called when the + * surface is destroyed or when new image data is attached using the + * same mime type. + * @closure: the data to be passed to the @destroy notifier + * + * Attach an image in the format @mime_type to @surface. To remove + * the data from a surface, call this function with same mime type + * and %NULL for @data. + * + * Since: 1.10 + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +cairo_surface_set_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char *data, + unsigned int length, + cairo_destroy_func_t destroy, + void *closure) +{ + cairo_status_t status; + cairo_mime_data_t *mime_data; + + if (unlikely (surface->status)) + return surface->status; + + status = _cairo_intern_string (&mime_type, -1); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + if (data != NULL) { + mime_data = malloc (sizeof (cairo_mime_data_t)); + if (unlikely (mime_data == NULL)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1); + + mime_data->data = (unsigned char *) data; + mime_data->length = length; + mime_data->destroy = destroy; + mime_data->closure = closure; + } else + mime_data = NULL; + + status = _cairo_user_data_array_set_data (&surface->mime_data, + (cairo_user_data_key_t *) mime_type, + mime_data, + _cairo_mime_data_destroy); + if (unlikely (status)) { + if (mime_data != NULL) + free (mime_data); + + return _cairo_surface_set_error (surface, status); + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_surface_set_mime_data); + +static void +_cairo_mime_data_reference (const void *key, void *elt, void *closure) +{ + cairo_mime_data_t *mime_data = elt; + + _cairo_reference_count_inc (&mime_data->ref_count); +} + +cairo_status_t +_cairo_surface_copy_mime_data (cairo_surface_t *dst, + cairo_surface_t *src) +{ + cairo_status_t status; + + if (dst->status) + return dst->status; + + if (src->status) + return _cairo_surface_set_error (dst, src->status); + + /* first copy the mime-data, discarding any already set on dst */ + status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data); + if (unlikely (status)) + return _cairo_surface_set_error (dst, status); + + /* now increment the reference counters for the copies */ + _cairo_user_data_array_foreach (&dst->mime_data, + _cairo_mime_data_reference, + NULL); + + return CAIRO_STATUS_SUCCESS; +} + +/** * _cairo_surface_set_font_options: * @surface: a #cairo_surface_t * @options: a #cairo_font_options_t object that contains the @@ -609,7 +919,7 @@ _cairo_surface_set_font_options (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, @@ -685,9 +995,12 @@ cairo_surface_flush (cairo_surface_t *surface) if (surface->finished) return; + /* update the current snapshots *before* the user updates the surface */ + _cairo_surface_detach_snapshots (surface); + if (surface->backend->flush) { status = surface->backend->flush (surface); - if (status) + if (unlikely (status)) status = _cairo_surface_set_error (surface, status); } } @@ -704,11 +1017,6 @@ slim_hidden_def (cairo_surface_flush); void cairo_surface_mark_dirty (cairo_surface_t *surface) { - if (surface->status) - return; - - assert (! surface->is_snapshot); - cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1); } @@ -740,13 +1048,18 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + /* The application *should* have called cairo_surface_flush() before + * modifying the surface independently of cairo (and thus having to + * call mark_dirty()). */ + assert (! _cairo_surface_has_snapshots (surface)); + /* 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 @@ -765,7 +1078,7 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, y + surface->device_transform.y0, width, height); - if (status) + if (unlikely (status)) status = _cairo_surface_set_error (surface, status); } } @@ -798,13 +1111,15 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + _cairo_surface_begin_modification (surface); + surface->device_transform.xx = sx; surface->device_transform.yy = sy; surface->device_transform.xy = 0.0; @@ -844,13 +1159,15 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + _cairo_surface_begin_modification (surface); + surface->device_transform.x0 = x_offset; surface->device_transform.y0 = y_offset; @@ -927,13 +1244,15 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } + _cairo_surface_begin_modification (surface); + surface->x_fallback_resolution = x_pixels_per_inch; surface->y_fallback_resolution = y_pixels_per_inch; } @@ -990,6 +1309,8 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, void **image_extra) { + cairo_status_t status; + if (surface->status) return surface->status; @@ -998,9 +1319,14 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, if (surface->backend->acquire_source_image == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; - return _cairo_surface_set_error (surface, - surface->backend->acquire_source_image (surface, - image_out, image_extra)); + status = surface->backend->acquire_source_image (surface, + image_out, image_extra); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); + + return CAIRO_STATUS_SUCCESS; } /** @@ -1059,20 +1385,27 @@ _cairo_surface_acquire_dest_image (cairo_surface_t *surface, cairo_rectangle_int_t *image_rect, void **image_extra) { + cairo_status_t status; + if (surface->status) return surface->status; - assert (!surface->finished); + assert (_cairo_surface_is_writable (surface)); if (surface->backend->acquire_dest_image == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; - return _cairo_surface_set_error (surface, - surface->backend->acquire_dest_image (surface, - interest_rect, - image_out, - image_rect, - image_extra)); + status = surface->backend->acquire_dest_image (surface, + interest_rect, + image_out, + image_rect, + image_extra); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); + + return CAIRO_STATUS_SUCCESS; } /** @@ -1094,7 +1427,7 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, cairo_rectangle_int_t *image_rect, void *image_extra) { - assert (!surface->finished); + assert (_cairo_surface_is_writable (surface)); if (surface->backend->release_dest_image) surface->backend->release_dest_image (surface, interest_rect, @@ -1105,6 +1438,7 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, * _cairo_surface_clone_similar: * @surface: a #cairo_surface_t * @src: the source image + * @content: target content mask * @src_x: extent for the rectangle in src we actually care about * @src_y: extent for the rectangle in src we actually care about * @width: extent for the rectangle in src we actually care about @@ -1124,6 +1458,7 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, cairo_status_t _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -1136,14 +1471,15 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_image_surface_t *image; void *image_extra; - if (surface->status) + if (unlikely (surface->status)) return surface->status; - if (surface->finished) + if (unlikely (surface->finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); if (surface->backend->clone_similar) { status = surface->backend->clone_similar (surface, src, + content, src_x, src_y, width, height, clone_offset_x, @@ -1151,11 +1487,40 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, clone_out); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (_cairo_surface_is_image (src)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* First check to see if we can replay to a similar surface */ + if (_cairo_surface_is_meta (src)) { + cairo_surface_t *similar; + + similar = cairo_surface_create_similar (surface, + src->content & content, + width, height); + status = similar->status; + if (unlikely (status)) + return status; + + cairo_surface_set_device_offset (similar, -src_x, -src_y); + + status = _cairo_meta_surface_replay (src, similar); + if (unlikely (status)) { + cairo_surface_destroy (similar); + return status; + } + + *clone_out = similar; + *clone_offset_x = src_x; + *clone_offset_y = src_y; + return CAIRO_STATUS_SUCCESS; + } + /* If we failed, try again with an image surface */ status = _cairo_surface_acquire_source_image (src, &image, &image_extra); if (status == CAIRO_STATUS_SUCCESS) { status = surface->backend->clone_similar (surface, &image->base, + content, src_x, src_y, width, height, clone_offset_x, @@ -1168,17 +1533,19 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, } /* If we're still unsupported, hit our fallback path to get a clone */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_surface_fallback_clone_similar (surface, src, + content, src_x, src_y, width, height, clone_offset_x, clone_offset_y, clone_out); + } /* We should never get UNSUPPORTED here, so if we have an error, bail. */ - if (status) + if (unlikely (status)) return status; /* Update the clone's device_transform (which the underlying surface @@ -1211,16 +1578,67 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_surface_t * _cairo_surface_snapshot (cairo_surface_t *surface) { + cairo_surface_t *snapshot; + cairo_status_t status; + if (surface->status) return _cairo_surface_create_in_error (surface->status); if (surface->finished) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - if (surface->backend->snapshot) - return surface->backend->snapshot (surface); + if (surface->snapshot_of != NULL) + return cairo_surface_reference (surface); + + snapshot = _cairo_surface_has_snapshot (surface, surface->backend); + if (snapshot != NULL) + return cairo_surface_reference (snapshot); + + if (surface->backend->snapshot != NULL) { + snapshot = surface->backend->snapshot (surface); + if (unlikely (snapshot->status)) + return snapshot; + + /* Is this surface just a proxy - e.g. paginated surfaces? */ + if (snapshot->backend != surface->backend) { + cairo_surface_t *previous; + + previous = _cairo_surface_has_snapshot (surface, + snapshot->backend); + if (previous != NULL) { + cairo_surface_destroy (snapshot); + return cairo_surface_reference (previous); + } + } + } + + if (snapshot == NULL) { + snapshot = _cairo_surface_has_snapshot (surface, + &_cairo_image_surface_backend); + if (snapshot != NULL) + return cairo_surface_reference (snapshot); + + snapshot = _cairo_surface_fallback_snapshot (surface); + if (unlikely (snapshot->status)) + return snapshot; + } + + status = _cairo_surface_copy_mime_data (snapshot, surface); + if (unlikely (status)) { + cairo_surface_destroy (snapshot); + return _cairo_surface_create_in_error (status); + } + + snapshot->device_transform = surface->device_transform; + snapshot->device_transform_inverse = surface->device_transform_inverse; + + status = _cairo_surface_attach_snapshot (surface, snapshot, NULL); + if (unlikely (status)) { + cairo_surface_destroy (snapshot); + return _cairo_surface_create_in_error (status); + } - return _cairo_surface_fallback_snapshot (surface); + return snapshot; } /** @@ -1254,8 +1672,8 @@ _cairo_surface_is_similar (cairo_surface_t *surface_a, cairo_status_t _cairo_surface_composite (cairo_operator_t op, - cairo_pattern_t *src, - cairo_pattern_t *mask, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, cairo_surface_t *dst, int src_x, int src_y, @@ -1268,6 +1686,11 @@ _cairo_surface_composite (cairo_operator_t op, { cairo_int_status_t status; + if (dst->status) + return dst->status; + + assert (_cairo_surface_is_writable (dst)); + if (mask) { /* These operators aren't interpreted the same way by the backends; * they are implemented in terms of other operators in cairo-gstate.c @@ -1275,14 +1698,6 @@ _cairo_surface_composite (cairo_operator_t op, assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); } - if (dst->status) - return dst->status; - - assert (! dst->is_snapshot); - - if (dst->finished) - return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); - if (dst->backend->composite) { status = dst->backend->composite (op, src, mask, dst, @@ -1332,10 +1747,7 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); + assert (_cairo_surface_is_writable (surface)); rect.x = x; rect.y = y; @@ -1364,8 +1776,7 @@ _cairo_surface_fill_region (cairo_surface_t *surface, const cairo_color_t *color, cairo_region_t *region) { - int num_boxes; - cairo_box_int_t *boxes = NULL; + int num_rects; cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_rectangle_int_t *rects = stack_rects; cairo_status_t status; @@ -1374,43 +1785,26 @@ _cairo_surface_fill_region (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + assert (_cairo_surface_is_writable (surface)); - num_boxes = _cairo_region_num_boxes (region); - - if (num_boxes == 0) + num_rects = cairo_region_num_rectangles (region); + if (num_rects == 0) return CAIRO_STATUS_SUCCESS; - /* handle the common case of a single box without allocation */ - if (num_boxes > 1) { - status = _cairo_region_get_boxes (region, &num_boxes, &boxes); - if (status) - return status; - - if (num_boxes > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (num_boxes, - sizeof (cairo_rectangle_int_t)); - if (!rects) { - _cairo_region_boxes_fini (region, boxes); - return _cairo_surface_set_error (surface, - CAIRO_STATUS_NO_MEMORY); - } + if (num_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (num_rects, + sizeof (cairo_rectangle_int_t)); + if (rects == NULL) { + return _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); } + } - for (i = 0; i < num_boxes; i++) { - rects[i].x = boxes[i].p1.x; - rects[i].y = boxes[i].p1.y; - rects[i].width = boxes[i].p2.x - boxes[i].p1.x; - rects[i].height = boxes[i].p2.y - boxes[i].p1.y; - } - } else - _cairo_region_get_extents (region, &rects[0]); + for (i = 0; i < num_rects; i++) + cairo_region_get_rectangle (region, i, &rects[i]); status = _cairo_surface_fill_rectangles (surface, op, - color, rects, num_boxes); - - if (boxes != NULL) - _cairo_region_boxes_fini (region, boxes); + color, rects, num_rects); if (rects != stack_rects) free (rects); @@ -1446,10 +1840,7 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); - - if (surface->finished) - return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); + assert (_cairo_surface_is_writable (surface)); if (num_rects == 0) return CAIRO_STATUS_SUCCESS; @@ -1469,69 +1860,80 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_status_t status; - cairo_pattern_t *dev_source; + cairo_pattern_union_t dev_source; if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source); - if (status) + status = _cairo_surface_copy_pattern_for_destination (&source, + surface, + &dev_source.base); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); if (surface->backend->paint) { - status = surface->backend->paint (surface, op, dev_source); + status = surface->backend->paint (surface, op, source, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; } - status = _cairo_surface_fallback_paint (surface, op, dev_source); + status = _cairo_surface_fallback_paint (surface, op, source); FINISH: - cairo_pattern_destroy (dev_source); + if (source == &dev_source.base) + _cairo_pattern_fini (&dev_source.base); return _cairo_surface_set_error (surface, status); } cairo_status_t -_cairo_surface_mask (cairo_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) +_cairo_surface_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { cairo_status_t status; - cairo_pattern_t *dev_source; - cairo_pattern_t *dev_mask; + cairo_pattern_union_t dev_source; + cairo_pattern_union_t dev_mask; if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source); - if (status) + status = _cairo_surface_copy_pattern_for_destination (&source, + surface, + &dev_source.base); + if (unlikely (status)) goto FINISH; - status = _cairo_surface_copy_pattern_for_destination (mask, surface, &dev_mask); - if (status) + status = _cairo_surface_copy_pattern_for_destination (&mask, + surface, + &dev_mask.base); + if (unlikely (status)) goto CLEANUP_SOURCE; if (surface->backend->mask) { - status = surface->backend->mask (surface, op, dev_source, dev_mask); + status = surface->backend->mask (surface, op, source, mask, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto CLEANUP_MASK; } - status = _cairo_surface_fallback_mask (surface, op, dev_source, dev_mask); + status = _cairo_surface_fallback_mask (surface, op, source, mask); CLEANUP_MASK: - cairo_pattern_destroy (dev_mask); + if (mask == &dev_mask.base) + _cairo_pattern_fini (&dev_mask.base); CLEANUP_SOURCE: - cairo_pattern_destroy (dev_source); + if (source == &dev_source.base) + _cairo_pattern_fini (&dev_source.base); FINISH: return _cairo_surface_set_error (surface, status); @@ -1540,62 +1942,78 @@ _cairo_surface_mask (cairo_surface_t *surface, cairo_status_t _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_operator_t fill_op, - cairo_pattern_t *fill_source, + const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, - cairo_pattern_t *stroke_source, + const cairo_pattern_t *stroke_source, cairo_stroke_style_t *stroke_style, cairo_matrix_t *stroke_ctm, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, - cairo_antialias_t stroke_antialias) + cairo_antialias_t stroke_antialias, + cairo_rectangle_int_t *extents) { cairo_status_t status; if (surface->status) return surface->status; + _cairo_surface_begin_modification (surface); + if (surface->backend->fill_stroke) { - cairo_pattern_t *dev_stroke_source; - cairo_pattern_t *dev_fill_source; + cairo_pattern_union_t dev_stroke_source; + cairo_pattern_union_t dev_fill_source; cairo_matrix_t dev_ctm = *stroke_ctm; cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; - status = _cairo_surface_copy_pattern_for_destination (stroke_source, surface, &dev_stroke_source); - if (status) + status = _cairo_surface_copy_pattern_for_destination (&stroke_source, + surface, + &dev_stroke_source.base); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); - status = _cairo_surface_copy_pattern_for_destination (fill_source, surface, &dev_fill_source); - if (status) { - cairo_pattern_destroy (dev_stroke_source); + status = _cairo_surface_copy_pattern_for_destination (&fill_source, + surface, + &dev_fill_source.base); + if (unlikely (status)) { + if (stroke_source == &dev_stroke_source.base) + _cairo_pattern_fini (&dev_stroke_source.base); + return _cairo_surface_set_error (surface, status); } - status = surface->backend->fill_stroke (surface, fill_op, dev_fill_source, - fill_rule, fill_tolerance, fill_antialias, - path, stroke_op, dev_stroke_source, stroke_style, - &dev_ctm, &dev_ctm_inverse, stroke_tolerance, - stroke_antialias); + status = surface->backend->fill_stroke (surface, + fill_op, fill_source, fill_rule, + fill_tolerance, fill_antialias, + path, + stroke_op, stroke_source, + stroke_style, + &dev_ctm, &dev_ctm_inverse, + stroke_tolerance, stroke_antialias, + extents); + + if (stroke_source == &dev_stroke_source.base) + _cairo_pattern_fini (&dev_stroke_source.base); - cairo_pattern_destroy (dev_stroke_source); - cairo_pattern_destroy (dev_fill_source); + if (fill_source == &dev_fill_source.base) + _cairo_pattern_fini (&dev_fill_source.base); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_surface_set_error (surface, status); } status = _cairo_surface_fill (surface, fill_op, fill_source, path, - fill_rule, fill_tolerance, fill_antialias); - if (status) + fill_rule, fill_tolerance, fill_antialias, NULL); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path, stroke_style, stroke_ctm, stroke_ctm_inverse, - stroke_tolerance, stroke_antialias); - if (status) + stroke_tolerance, stroke_antialias, NULL); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); return CAIRO_STATUS_SUCCESS; @@ -1604,16 +2022,17 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_status_t _cairo_surface_stroke (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_status_t status; - cairo_pattern_t *dev_source; + cairo_pattern_union_t dev_source; cairo_path_fixed_t *dev_path = path; cairo_path_fixed_t real_dev_path; cairo_matrix_t dev_ctm = *ctm; @@ -1622,23 +2041,25 @@ _cairo_surface_stroke (cairo_surface_t *surface, if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source); - if (status) + status = _cairo_surface_copy_pattern_for_destination (&source, + surface, + &dev_source.base); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); if (surface->backend->stroke) { - status = surface->backend->stroke (surface, op, dev_source, + status = surface->backend->stroke (surface, op, source, path, stroke_style, &dev_ctm, &dev_ctm_inverse, - tolerance, antialias); + tolerance, antialias, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; } - status = _cairo_surface_fallback_stroke (surface, op, dev_source, + status = _cairo_surface_fallback_stroke (surface, op, source, path, stroke_style, &dev_ctm, &dev_ctm_inverse, tolerance, antialias); @@ -1646,7 +2067,9 @@ _cairo_surface_stroke (cairo_surface_t *surface, FINISH: if (dev_path == &real_dev_path) _cairo_path_fixed_fini (&real_dev_path); - cairo_pattern_destroy (dev_source); + + if (source == &dev_source.base) + _cairo_pattern_fini (&dev_source.base); return _cairo_surface_set_error (surface, status); } @@ -1654,46 +2077,50 @@ _cairo_surface_stroke (cairo_surface_t *surface, cairo_status_t _cairo_surface_fill (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_status_t status; - cairo_pattern_t *dev_source; + cairo_pattern_union_t dev_source; if (surface->status) return surface->status; - assert (! surface->is_snapshot); + _cairo_surface_begin_modification (surface); - status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source); - if (status) + status = _cairo_surface_copy_pattern_for_destination (&source, + surface, + &dev_source.base); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); if (surface->backend->fill) { - status = surface->backend->fill (surface, op, dev_source, + status = surface->backend->fill (surface, op, source, path, fill_rule, - tolerance, antialias); + tolerance, antialias, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; } - status = _cairo_surface_fallback_fill (surface, op, dev_source, + status = _cairo_surface_fallback_fill (surface, op, source, path, fill_rule, tolerance, antialias); FINISH: - cairo_pattern_destroy (dev_source); + if (source == &dev_source.base) + _cairo_pattern_fini (&dev_source.base); return _cairo_surface_set_error (surface, status); } cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *dst, cairo_antialias_t antialias, int src_x, @@ -1707,18 +2134,15 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, { cairo_int_status_t status; - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); - if (dst->status) return dst->status; - assert (! dst->is_snapshot); + assert (_cairo_surface_is_writable (dst)); - if (dst->finished) - return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); + /* These operators aren't interpreted the same way by the backends; + * they are implemented in terms of other operators in cairo-gstate.c + */ + assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); if (dst->backend->composite_trapezoids) { status = dst->backend->composite_trapezoids (op, @@ -1741,6 +2165,59 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, traps, num_traps)); } +cairo_span_renderer_t * +_cairo_surface_create_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + assert (dst->snapshot_of == NULL); + + if (dst->status) + return _cairo_span_renderer_create_in_error (dst->status); + + if (dst->finished) + return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + + if (dst->backend->create_span_renderer) { + return dst->backend->create_span_renderer (op, + pattern, dst, + antialias, + rects); + } + ASSERT_NOT_REACHED; + return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED); +} + +cairo_bool_t +_cairo_surface_check_span_renderer (cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects) +{ + cairo_int_status_t status; + + assert (dst->snapshot_of == NULL); + + if (dst->status) + return FALSE; + + if (dst->finished) { + status = _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); + return FALSE; + } + + if (dst->backend->check_span_renderer) { + return dst->backend->check_span_renderer (op, + pattern, dst, + antialias, + rects); + } + return FALSE; +} + /** * cairo_surface_copy_page: * @surface: a #cairo_surface_t @@ -1763,7 +2240,7 @@ cairo_surface_copy_page (cairo_surface_t *surface) if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status_ignored = _cairo_surface_set_error (surface, @@ -1800,7 +2277,7 @@ cairo_surface_show_page (cairo_surface_t *surface) if (surface->status) return; - assert (! surface->is_snapshot); + assert (surface->snapshot_of == NULL); if (surface->finished) { status_ignored = _cairo_surface_set_error (surface, @@ -1885,13 +2362,13 @@ _cairo_surface_reset_clip (cairo_surface_t *surface) CAIRO_FILL_RULE_WINDING, 0, CAIRO_ANTIALIAS_DEFAULT); - if (status) + if (unlikely (status)) return _cairo_surface_set_error (surface, status); } if (surface->backend->set_clip_region != NULL) { status = surface->backend->set_clip_region (surface, NULL); - if (status) + if (unlikely (status)) return _cairo_surface_set_error (surface, status); } @@ -1911,20 +2388,25 @@ _cairo_surface_reset_clip (cairo_surface_t *surface) cairo_status_t _cairo_surface_set_clip_region (cairo_surface_t *surface, cairo_region_t *region, - unsigned int serial) + unsigned int serial) { cairo_status_t status; - + if (surface->status) return surface->status; - assert (surface->backend->set_clip_region != NULL); + if (surface->finished) + return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED); - surface->current_clip_serial = serial; + assert (surface->backend->set_clip_region != NULL); status = surface->backend->set_clip_region (surface, region); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); - return _cairo_surface_set_error (surface, status); + surface->current_clip_serial = serial; + + return CAIRO_STATUS_SUCCESS; } cairo_int_status_t @@ -1967,7 +2449,7 @@ _cairo_surface_set_clip_path_recursive (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; status = _cairo_surface_set_clip_path_recursive (surface, clip_path->prev); - if (status) + if (unlikely (status)) return status; return _cairo_surface_intersect_clip_path (surface, @@ -2006,11 +2488,11 @@ _cairo_surface_set_clip_path (cairo_surface_t *surface, CAIRO_FILL_RULE_WINDING, 0, CAIRO_ANTIALIAS_DEFAULT); - if (status) + if (unlikely (status)) return _cairo_surface_set_error (surface, status); status = _cairo_surface_set_clip_path_recursive (surface, clip_path); - if (status) + if (unlikely (status)) return _cairo_surface_set_error (surface, status); surface->current_clip_serial = serial; @@ -2089,7 +2571,7 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) if (surface->backend->set_clip_region != NULL) return _cairo_surface_set_clip_region (surface, - &clip->region, + clip->region, clip->serial); } else { if (clip->path) @@ -2097,9 +2579,9 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) clip->path, clip->serial); - if (clip->has_region) + if (clip->region) return _cairo_surface_set_clip_region (surface, - &clip->region, + clip->region, clip->serial); } } @@ -2218,7 +2700,7 @@ slim_hidden_def (cairo_surface_has_show_text_glyphs); cairo_status_t _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -2226,24 +2708,25 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_scaled_font_t *dev_scaled_font = scaled_font; - cairo_pattern_t *dev_source; + cairo_pattern_union_t dev_source; if (surface->status) return surface->status; - assert (! surface->is_snapshot); - - if (!num_glyphs && !utf8_len) + if (num_glyphs == 0 && utf8_len == 0) return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_copy_pattern_for_destination (source, + _cairo_surface_begin_modification (surface); + + status = _cairo_surface_copy_pattern_for_destination (&source, surface, - &dev_source); - if (status) + &dev_source.base); + if (unlikely (status)) return _cairo_surface_set_error (surface, status); if (_cairo_surface_has_device_transform (surface) && @@ -2262,8 +2745,10 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, &font_options); } status = cairo_scaled_font_status (dev_scaled_font); - if (status) { - cairo_pattern_destroy (dev_source); + if (unlikely (status)) { + if (source == &dev_source.base) + _cairo_pattern_fini (&dev_source.base); + return _cairo_surface_set_error (surface, status); } @@ -2275,18 +2760,20 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, /* A real show_text_glyphs call. Try show_text_glyphs backend * method first */ if (surface->backend->show_text_glyphs) { - status = surface->backend->show_text_glyphs (surface, op, dev_source, + status = surface->backend->show_text_glyphs (surface, op, + source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - dev_scaled_font); + dev_scaled_font, extents); } if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->backend->show_glyphs) { int remaining_glyphs = num_glyphs; - status = surface->backend->show_glyphs (surface, op, dev_source, + status = surface->backend->show_glyphs (surface, op, + source, glyphs, num_glyphs, dev_scaled_font, - &remaining_glyphs); + &remaining_glyphs, extents); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) @@ -2296,10 +2783,11 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, /* A mere show_glyphs call. Try show_glyphs backend method first */ if (surface->backend->show_glyphs) { int remaining_glyphs = num_glyphs; - status = surface->backend->show_glyphs (surface, op, dev_source, + status = surface->backend->show_glyphs (surface, op, + source, glyphs, num_glyphs, dev_scaled_font, - &remaining_glyphs); + &remaining_glyphs, extents); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) @@ -2313,23 +2801,26 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, * implies that UTF-8 is not NULL, unless the text is * zero-length). */ - status = surface->backend->show_text_glyphs (surface, op, dev_source, + status = surface->backend->show_text_glyphs (surface, op, + source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - dev_scaled_font); + dev_scaled_font, extents); } } if (status == CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_surface_fallback_show_glyphs (surface, op, dev_source, + status = _cairo_surface_fallback_show_glyphs (surface, op, + source, glyphs, num_glyphs, dev_scaled_font); if (dev_scaled_font != scaled_font) cairo_scaled_font_destroy (dev_scaled_font); - cairo_pattern_destroy (dev_source); + if (source == &dev_source.base) + _cairo_pattern_fini (&dev_source.base); return _cairo_surface_set_error (surface, status); } @@ -2342,7 +2833,7 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_status_t _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *dst, int source_x, int source_y, @@ -2358,10 +2849,7 @@ _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, if (dst->status) return dst->status; - assert (! dst->is_snapshot); - - if (dst->finished) - return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED); + assert (_cairo_surface_is_writable (dst)); if (dst->backend->old_show_glyphs) { status = dst->backend->old_show_glyphs (scaled_font, @@ -2386,53 +2874,44 @@ _cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, unsigned int height) { cairo_rectangle_int_t dst_rectangle; - cairo_rectangle_int_t drawn_rectangle; - cairo_bool_t has_drawn_region = FALSE; - cairo_region_t drawn_region; - cairo_region_t clear_region; + cairo_region_t *clear_region; cairo_status_t status; - /* The area that was drawn is the area in the destination rectangle but not within - * the source or the mask. + /* The area that was drawn is the area in the destination rectangle but + * not within the source or the mask. */ dst_rectangle.x = dst_x; dst_rectangle.y = dst_y; dst_rectangle.width = width; dst_rectangle.height = height; - _cairo_region_init_rect (&clear_region, &dst_rectangle); - drawn_rectangle = dst_rectangle; + clear_region = cairo_region_create_rectangle (&dst_rectangle); + status = clear_region->status; + if (unlikely (status)) + goto CLEANUP_REGIONS; if (src_rectangle) { - if (! _cairo_rectangle_intersect (&drawn_rectangle, src_rectangle)) + if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle)) goto EMPTY; } if (mask_rectangle) { - if (! _cairo_rectangle_intersect (&drawn_rectangle, mask_rectangle)) + if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle)) goto EMPTY; } - /* Now compute the area that is in dst_rectangle but not in drawn_rectangle - */ - _cairo_region_init_rect (&drawn_region, &drawn_rectangle); - has_drawn_region = TRUE; - - status = _cairo_region_subtract (&clear_region, - &clear_region, - &drawn_region); - if (status) + /* Now compute the area that is in dst but not drawn */ + status = cairo_region_subtract_rectangle (clear_region, &dst_rectangle); + if (unlikely (status)) goto CLEANUP_REGIONS; EMPTY: status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_SOURCE, CAIRO_COLOR_TRANSPARENT, - &clear_region); + clear_region); CLEANUP_REGIONS: - if (has_drawn_region) - _cairo_region_fini (&drawn_region); - _cairo_region_fini (&clear_region); + cairo_region_destroy (clear_region); return _cairo_surface_set_error (dst, status); } @@ -2485,7 +2964,7 @@ _cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, if (dst->status) return dst->status; - assert (! dst->is_snapshot); + assert (_cairo_surface_is_writable (dst)); /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, * non-repeating sources and masks. Other sources and masks can be ignored. @@ -2563,7 +3042,7 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, if (dst->status) return dst->status; - assert (! dst->is_snapshot); + assert (_cairo_surface_is_writable (dst)); /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, * non-repeating sources and masks. Other sources and masks can be ignored. @@ -2594,27 +3073,30 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, * _cairo_surface_copy_pattern_for_destination * @pattern: the pattern to copy * @destination: the destination surface for which the pattern is being copied - * @pattern_out: the location to hold the copy + * @pattern_copy: the location to hold the copy * * Copies the given pattern, taking into account device scale and offsets * of the destination surface. */ static cairo_status_t -_cairo_surface_copy_pattern_for_destination (const cairo_pattern_t *pattern, +_cairo_surface_copy_pattern_for_destination (const cairo_pattern_t **pattern, cairo_surface_t *destination, - cairo_pattern_t **pattern_out) + cairo_pattern_t *pattern_copy) { cairo_status_t status; - status = _cairo_pattern_create_copy (pattern_out, pattern); - if (status) + if (! _cairo_surface_has_device_transform (destination)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pattern_init_copy (pattern_copy, *pattern); + if (unlikely (status)) return status; - if (_cairo_surface_has_device_transform (destination)) { - _cairo_pattern_transform (*pattern_out, - &destination->device_transform_inverse); - } + _cairo_pattern_transform (pattern_copy, + &destination->device_transform_inverse); + + *pattern = pattern_copy; return CAIRO_STATUS_SUCCESS; } @@ -2662,7 +3144,10 @@ _cairo_surface_create_in_error (cairo_status_t status) return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error; case CAIRO_STATUS_INVALID_STRIDE: return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride; + case CAIRO_STATUS_INVALID_SIZE: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_size; case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; /* fall-through */ case CAIRO_STATUS_INVALID_RESTORE: @@ -2687,6 +3172,7 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_CLUSTERS: case CAIRO_STATUS_INVALID_SLANT: case CAIRO_STATUS_INVALID_WEIGHT: + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index f392c8e..7a35fab 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -43,12 +43,13 @@ #include "cairoint.h" #include "cairo-svg.h" #include "cairo-analysis-surface-private.h" -#include "cairo-svg-surface-private.h" +#include "cairo-image-info-private.h" #include "cairo-meta-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-path-fixed-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-svg-surface-private.h" typedef struct cairo_svg_page cairo_svg_page_t; @@ -284,7 +285,7 @@ cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, cairo_status_t status; status = _extract_svg_surface (abstract_surface, &surface); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (abstract_surface, status); return; } @@ -346,7 +347,7 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, cairo_status_t status, status_ignored; surface = malloc (sizeof (cairo_svg_surface_t)); - if (surface == NULL) + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_svg_surface_backend, @@ -365,7 +366,7 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, surface->xml_node = _cairo_memory_stream_create (); status = _cairo_output_stream_get_status (surface->xml_node); - if (status) + if (unlikely (status)) goto CLEANUP; _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); @@ -377,7 +378,7 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, "fill:rgb(0,0,0);\"/>\n", width, height); status = _cairo_output_stream_get_status (surface->xml_node); - if (status) + if (unlikely (status)) goto CLEANUP; } @@ -420,7 +421,7 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, status = _cairo_svg_document_create (stream, width, height, version, &document); - if (status) { + if (unlikely (status)) { surface = _cairo_surface_create_in_error (status); /* consume the output stream on behalf of caller */ status = _cairo_output_stream_destroy (stream); @@ -481,7 +482,7 @@ _cairo_svg_surface_copy_page (void *abstract_surface) cairo_svg_page_t *page; page = _cairo_svg_surface_store_page (surface); - if (page == NULL) + if (unlikely (page == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_memory_stream_copy (page->xml_node, surface->xml_node); @@ -495,7 +496,7 @@ _cairo_svg_surface_show_page (void *abstract_surface) { cairo_svg_surface_t *surface = abstract_surface; - if (_cairo_svg_surface_store_page (surface) == NULL) + if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; @@ -521,14 +522,14 @@ _cairo_svg_surface_emit_transform (cairo_output_stream_t *output, matrix.x0, matrix.y0); } -typedef struct -{ +typedef struct { cairo_output_stream_t *output; cairo_matrix_t *ctm_inverse; } svg_path_info_t; static cairo_status_t -_cairo_svg_path_move_to (void *closure, cairo_point_t *point) +_cairo_svg_path_move_to (void *closure, + const cairo_point_t *point) { svg_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); @@ -543,7 +544,8 @@ _cairo_svg_path_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cairo_svg_path_line_to (void *closure, cairo_point_t *point) +_cairo_svg_path_line_to (void *closure, + const cairo_point_t *point) { svg_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); @@ -559,9 +561,9 @@ _cairo_svg_path_line_to (void *closure, cairo_point_t *point) static cairo_status_t _cairo_svg_path_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) { svg_path_info_t *info = closure; double bx = _cairo_fixed_to_double (b->x); @@ -613,7 +615,7 @@ _cairo_svg_surface_emit_path (cairo_output_stream_t *output, _cairo_svg_path_curve_to, _cairo_svg_path_close_path, &info); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (output, "\""); @@ -634,14 +636,14 @@ _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, CAIRO_SCALED_GLYPH_INFO_METRICS| CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_glyphs, "<path style=\"stroke:none;\" "); status = _cairo_svg_surface_emit_path (document->xml_node_glyphs, scaled_glyph->path, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_glyphs, @@ -655,8 +657,8 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, unsigned long glyph_index) { - cairo_image_surface_t *image; cairo_scaled_glyph_t *scaled_glyph; + cairo_image_surface_t *image; cairo_status_t status; uint8_t *row, *byte; int rows, cols; @@ -664,18 +666,17 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, status = _cairo_scaled_glyph_lookup (scaled_font, glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); - if (status) + if (unlikely (status)) return status; - image = scaled_glyph->surface; - if (image->format != CAIRO_FORMAT_A1) { - image = _cairo_image_surface_clone (image, CAIRO_FORMAT_A1); - if (cairo_surface_status (&image->base)) - return cairo_surface_status (&image->base); - } + image = _cairo_image_surface_coerce (scaled_glyph->surface, + CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; _cairo_output_stream_printf (document->xml_node_glyphs, "<g"); _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform", @@ -696,8 +697,7 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, } _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n"); - if (image != scaled_glyph->surface) - cairo_surface_destroy (&image->base); + cairo_surface_destroy (&image->base); return CAIRO_STATUS_SUCCESS; } @@ -723,7 +723,7 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, status = _cairo_svg_document_emit_bitmap_glyph_data (document, scaled_font, scaled_font_glyph_index); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n"); @@ -745,7 +745,7 @@ _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset, font_subset->scaled_font, font_subset->glyphs[i], font_subset->font_id, i); - if (status) + if (unlikely (status)) break; } _cairo_scaled_font_thaw_cache (font_subset->scaled_font); @@ -761,7 +761,7 @@ _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, _cairo_svg_document_emit_font_subset, document); - if (status) + if (unlikely (status)) goto FAIL; status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, @@ -976,12 +976,94 @@ base64_write_func (void *closure, } static cairo_int_status_t +_cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + const unsigned char *mime_data; + unsigned int mime_data_length; + cairo_image_info_t image_info; + base64_write_closure_t info; + cairo_status_t status; + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + status = base64_write_func (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static cairo_int_status_t +_cairo_surface_base64_encode_png (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + const unsigned char *mime_data; + unsigned int mime_data_length; + base64_write_closure_t info; + cairo_status_t status; + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (unlikely (surface->status)) + return surface->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (output, "data:image/png;base64,"); + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + status = base64_write_func (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static cairo_int_status_t _cairo_surface_base64_encode (cairo_surface_t *surface, cairo_output_stream_t *output) { cairo_status_t status; base64_write_closure_t info; - unsigned int i; + + status = _cairo_surface_base64_encode_jpeg (surface, output); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_base64_encode_png (surface, output); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; info.output = output; info.in_mem = 0; @@ -992,12 +1074,11 @@ _cairo_surface_base64_encode (cairo_surface_t *surface, status = cairo_surface_write_to_png_stream (surface, base64_write_func, (void *) &info); - if (status) + if (unlikely (status)) return status; if (info.in_mem > 0) { - for (i = info.in_mem; i < 3; i++) - info.src[i] = '\x0'; + memset (info.src + info.in_mem, 0, 3 - info.in_mem); info.trailing = 3 - info.in_mem; info.in_mem = 3; status = base64_write_func (&info, NULL, 0); @@ -1046,7 +1127,7 @@ _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output cairo_matrix_t p2u; status = _cairo_surface_get_extents (pattern->surface, &extents); - if (status) + if (unlikely (status)) return status; p2u = pattern->base.matrix; @@ -1134,7 +1215,7 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, document->owner->y_fallback_resolution); status = _cairo_meta_surface_replay (&meta->base, paginated_surface); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (&meta->base); cairo_surface_destroy (paginated_surface); return status; @@ -1142,7 +1223,7 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, cairo_surface_show_page (paginated_surface); status = cairo_surface_status (paginated_surface); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (&meta->base); cairo_surface_destroy (paginated_surface); return status; @@ -1151,7 +1232,7 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, new_snapshot.meta = meta; new_snapshot.id = svg_surface->id; status = _cairo_array_append (&document->meta_snapshots, &new_snapshot); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (&meta->base); cairo_surface_destroy (paginated_surface); return status; @@ -1188,7 +1269,7 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document, page_set = &svg_surface->page_set; if (_cairo_memory_stream_length (contents) > 0) { - if (_cairo_svg_surface_store_page (svg_surface) == NULL) { + if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) { cairo_surface_destroy (paginated_surface); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -1241,7 +1322,7 @@ _cairo_svg_surface_emit_composite_meta_pattern (cairo_output_stream_t *output, meta_surface = (cairo_meta_surface_t *) pattern->surface; status = _cairo_svg_surface_emit_meta_surface (document, meta_surface, &id); - if (status) + if (unlikely (status)) return status; if (pattern_id != invalid_pattern_id) { @@ -1327,7 +1408,7 @@ _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface, status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs, surface, CAIRO_OPERATOR_SOURCE, pattern, pattern_id, parent_matrix, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (style, @@ -1369,7 +1450,7 @@ _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, if (emulate_reflect || reverse_stops) { n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops; stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t)); - if (stops == NULL) + if (unlikely (stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < pattern->n_stops; i++) { @@ -1554,7 +1635,7 @@ _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, &pattern->base, 0.0, FALSE, FALSE); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_defs, @@ -1732,7 +1813,7 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, &pattern->base, offset, reverse_stops, emulate_reflect); - if (status) + if (unlikely (status)) return status; if (pattern->base.base.extend == CAIRO_EXTEND_NONE) @@ -1757,7 +1838,7 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, static cairo_status_t _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_output_stream_t *output, cairo_bool_t is_stroke, const cairo_matrix_t *parent_matrix) @@ -1783,12 +1864,12 @@ _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, } static cairo_status_t -_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_fill_rule_t fill_rule, - cairo_matrix_t *parent_matrix) +_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_fill_rule_t fill_rule, + cairo_matrix_t *parent_matrix) { _cairo_output_stream_printf (output, "fill-rule:%s;", @@ -1799,12 +1880,12 @@ _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, } static cairo_status_t -_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, - cairo_svg_surface_t *surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_stroke_style_t *stroke_style, - cairo_matrix_t *parent_matrix) +_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_stroke_style_t *stroke_style, + cairo_matrix_t *parent_matrix) { cairo_status_t status; const char *line_cap, *line_join; @@ -1847,7 +1928,7 @@ _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, line_join); status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); - if (status) + if (unlikely (status)) return status; _cairo_svg_surface_emit_operator_for_style (output, surface, op); @@ -1879,18 +1960,19 @@ _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, static cairo_int_status_t _cairo_svg_surface_fill_stroke (void *abstract_surface, cairo_operator_t fill_op, - cairo_pattern_t *fill_source, + const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, - cairo_pattern_t *stroke_source, + const cairo_pattern_t *stroke_source, cairo_stroke_style_t *stroke_style, cairo_matrix_t *stroke_ctm, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, - cairo_antialias_t stroke_antialias) + cairo_antialias_t stroke_antialias, + cairo_rectangle_int_t *extents) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; @@ -1898,18 +1980,18 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "<path style=\""); status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op, fill_source, fill_rule, stroke_ctm_inverse); - if (status) + if (unlikely (status)) return status; status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op, stroke_source, stroke_style, stroke_ctm_inverse); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "\" "); status = _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); - if (status) + if (unlikely (status)) return status; _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); @@ -1921,11 +2003,12 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, static cairo_int_status_t _cairo_svg_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; @@ -1937,13 +2020,13 @@ _cairo_svg_surface_fill (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;"); status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "\" "); status = _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "/>\n"); @@ -1974,8 +2057,8 @@ static cairo_status_t _cairo_svg_surface_emit_paint (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask_source, + const cairo_pattern_t *source, + const cairo_pattern_t *mask_source, const char *extra_attributes) { cairo_status_t status; @@ -1997,7 +2080,7 @@ _cairo_svg_surface_emit_paint (cairo_output_stream_t *output, surface->width, surface->height); _cairo_svg_surface_emit_operator_for_style (output, surface, op); status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (output, "stroke:none;\""); @@ -2013,7 +2096,8 @@ _cairo_svg_surface_emit_paint (cairo_output_stream_t *output, static cairo_int_status_t _cairo_svg_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; @@ -2033,7 +2117,7 @@ _cairo_svg_surface_paint (void *abstract_surface, * and an optimization in meta surface. */ if (surface->clip_level == 0 && op == CAIRO_OPERATOR_CLEAR) { status = _cairo_output_stream_destroy (surface->xml_node); - if (status) { + if (unlikely (status)) { surface->xml_node = NULL; return status; } @@ -2065,9 +2149,10 @@ _cairo_svg_surface_paint (void *abstract_surface, static cairo_int_status_t _cairo_svg_surface_mask (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; @@ -2095,8 +2180,8 @@ _cairo_svg_surface_mask (void *abstract_surface, assert (_cairo_svg_surface_operation_supported (surface, op, source)); assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); - if (cairo_pattern_get_type (mask) == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t*) mask; + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; cairo_content_t content = cairo_surface_get_content (surface_pattern->surface); if (content == CAIRO_CONTENT_ALPHA) discard_filter = TRUE; @@ -2120,7 +2205,7 @@ _cairo_svg_surface_mask (void *abstract_surface, mask_id, discard_filter ? "" : " <g filter=\"url(#alpha)\">\n"); status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); - if (status) { + if (unlikely (status)) { cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); return status; (void) ignore; @@ -2133,13 +2218,13 @@ _cairo_svg_surface_mask (void *abstract_surface, _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); status = _cairo_output_stream_destroy (mask_stream); - if (status) + if (unlikely (status)) return status; snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", mask_id); status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -2148,13 +2233,14 @@ _cairo_svg_surface_mask (void *abstract_surface, static cairo_int_status_t _cairo_svg_surface_stroke (void *abstract_dst, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, cairo_matrix_t *ctm, cairo_matrix_t *ctm_inverse, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_svg_surface_t *surface = abstract_dst; cairo_status_t status; @@ -2167,13 +2253,13 @@ _cairo_svg_surface_stroke (void *abstract_dst, _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;"); status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op, source, stroke_style, ctm_inverse); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "\" "); status = _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); - if (status) + if (unlikely (status)) return status; _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); @@ -2185,11 +2271,12 @@ _cairo_svg_surface_stroke (void *abstract_dst, static cairo_int_status_t _cairo_svg_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; @@ -2215,7 +2302,7 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, _cairo_output_stream_printf (surface->xml_node, "<g style=\""); status = _cairo_svg_surface_emit_pattern (surface, pattern, surface->xml_node, FALSE, NULL); - if (status) + if (unlikely (status)) return status; _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); @@ -2235,7 +2322,7 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, goto FALLBACK; } - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, @@ -2255,13 +2342,13 @@ FALLBACK: status = _cairo_scaled_font_glyph_path (scaled_font,(cairo_glyph_t *) glyphs, num_glyphs, &path); - if (status) { + if (unlikely (status)) { _cairo_path_fixed_fini (&path); return status; } status = _cairo_svg_surface_fill (abstract_surface, op, pattern, - &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL); + &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL, NULL); _cairo_path_fixed_fini (&path); @@ -2293,7 +2380,7 @@ _cairo_svg_surface_intersect_clip_path (void *dst, " <path ", document->clip_id); status = _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_defs, @@ -2336,6 +2423,8 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { NULL, /* _cairo_svg_surface_composite, */ NULL, /* _cairo_svg_surface_fill_rectangles, */ NULL, /* _cairo_svg_surface_composite_trapezoids,*/ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ _cairo_svg_surface_copy_page, _cairo_svg_surface_show_page, NULL, /* set_clip_region */ @@ -2372,12 +2461,12 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, return output_stream->status; document = malloc (sizeof (cairo_svg_document_t)); - if (document == NULL) + if (unlikely (document == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* The use of defs for font glyphs imposes no per-subset limit. */ document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); - if (document->font_subsets == NULL) { + if (unlikely (document->font_subsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_DOCUMENT; } @@ -2399,12 +2488,12 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, document->xml_node_defs = _cairo_memory_stream_create (); status = _cairo_output_stream_get_status (document->xml_node_defs); - if (status) + if (unlikely (status)) goto CLEANUP_NODE_DEFS; document->xml_node_glyphs = _cairo_memory_stream_create (); status = _cairo_output_stream_get_status (document->xml_node_glyphs); - if (status) + if (unlikely (status)) goto CLEANUP_NODE_GLYPHS; document->alpha_filter = FALSE; @@ -2521,7 +2610,7 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); if (surface->xml_node != NULL && _cairo_memory_stream_length (surface->xml_node) > 0) { - if (_cairo_svg_surface_store_page (surface) == NULL) { + if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) { if (status == CAIRO_STATUS_SUCCESS) status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c new file mode 100644 index 0000000..4abafc4 --- /dev/null +++ b/src/cairo-tor-scan-converter.c @@ -0,0 +1,1991 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* glitter-paths - polygon scan converter + * + * Copyright (c) 2008 M Joonas Pihlaja + * Copyright (c) 2007 David Turner + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/* This is the Glitter paths scan converter incorporated into cairo. + * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 + * of + * + * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths + */ +/* Glitter-paths is a stand alone polygon rasteriser derived from + * David Turner's reimplementation of Tor Anderssons's 15x17 + * supersampling rasteriser from the Apparition graphics library. The + * main new feature here is cheaply choosing per-scan line between + * doing fully analytical coverage computation for an entire row at a + * time vs. using a supersampling approach. + * + * David Turner's code can be found at + * + * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2 + * + * In particular this file incorporates large parts of ftgrays_tor10.h + * from raster-comparison-20070813.tar.bz2 + */ +/* Overview + * + * A scan converter's basic purpose to take polygon edges and convert + * them into an RLE compressed A8 mask. This one works in two phases: + * gathering edges and generating spans. + * + * 1) As the user feeds the scan converter edges they are vertically + * clipped and bucketted into a _polygon_ data structure. The edges + * are also snapped from the user's coordinates to the subpixel grid + * coordinates used during scan conversion. + * + * user + * | + * | edges + * V + * polygon buckets + * + * 2) Generating spans works by performing a vertical sweep of pixel + * rows from top to bottom and maintaining an _active_list_ of edges + * that intersect the row. From the active list the fill rule + * determines which edges are the left and right edges of the start of + * each span, and their contribution is then accumulated into a pixel + * coverage list (_cell_list_) as coverage deltas. Once the coverage + * deltas of all edges are known we can form spans of constant pixel + * coverage by summing the deltas during a traversal of the cell list. + * At the end of a pixel row the cell list is sent to a coverage + * blitter for rendering to some target surface. + * + * The pixel coverages are computed by either supersampling the row + * and box filtering a mono rasterisation, or by computing the exact + * coverages of edges in the active list. The supersampling method is + * used whenever some edge starts or stops within the row or there are + * edge intersections in the row. + * + * polygon bucket for \ + * current pixel row | + * | | + * | activate new edges | Repeat GRID_Y times if we + * V \ are supersampling this row, + * active list / or just once if we're computing + * | | analytical coverage. + * | coverage deltas | + * V | + * pixel coverage list / + * | + * V + * coverage blitter + */ +#include "cairoint.h" +#include "cairo-spans-private.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +/*------------------------------------------------------------------------- + * cairo specific config + */ +#define I static + +/* Prefer cairo's status type. */ +#define GLITTER_HAVE_STATUS_T 1 +#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS +#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY +typedef cairo_status_t glitter_status_t; + +/* The input coordinate scale and the rasterisation grid scales. */ +#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS +#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS +#define GRID_Y 15 + +/* Set glitter up to use a cairo span renderer to do the coverage + * blitting. */ +struct pool; +struct cell_list; + +static glitter_status_t +blit_with_span_renderer( + struct cell_list *coverages, + cairo_span_renderer_t *span_renderer, + struct pool *span_pool, + int y, + int xmin, + int xmax); + +#define GLITTER_BLIT_COVERAGES_ARGS \ + cairo_span_renderer_t *span_renderer, \ + struct pool *span_pool + +#define GLITTER_BLIT_COVERAGES(cells, y, xmin, xmax) do { \ + cairo_status_t status = blit_with_span_renderer (cells, \ + span_renderer, \ + span_pool, \ + y, xmin, xmax); \ + if (unlikely (status)) \ + return status; \ +} while (0) + +/*------------------------------------------------------------------------- + * glitter-paths.h + */ + +/* "Input scaled" numbers are fixed precision reals with multiplier + * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as + * pixel scaled numbers. These get converted to the internal grid + * scaled numbers as soon as possible. Internal overflow is possible + * if GRID_X/Y inside glitter-paths.c is larger than + * 1<<GLITTER_INPUT_BITS. */ +#ifndef GLITTER_INPUT_BITS +# define GLITTER_INPUT_BITS 8 +#endif +#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS) +typedef int glitter_input_scaled_t; + +#if !GLITTER_HAVE_STATUS_T +typedef enum { + GLITTER_STATUS_SUCCESS = 0, + GLITTER_STATUS_NO_MEMORY +} glitter_status_t; +#endif + +#ifndef I +# define I /*static*/ +#endif + +/* Opaque type for scan converting. */ +typedef struct glitter_scan_converter glitter_scan_converter_t; + +/* Reset a scan converter to accept polygon edges and set the clip box + * in pixels. Allocates O(ymax-ymin) bytes of memory. The clip box + * is set to integer pixel coordinates xmin <= x < xmax, ymin <= y < + * ymax. */ +I glitter_status_t +glitter_scan_converter_reset( + glitter_scan_converter_t *converter, + int xmin, int ymin, + int xmax, int ymax); + +/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan + * converter. The coordinates represent pixel positions scaled by + * 2**GLITTER_PIXEL_BITS. If this function fails then the scan + * converter should be reset or destroyed. Dir must be +1 or -1, + * with the latter reversing the orientation of the edge. */ +I glitter_status_t +glitter_scan_converter_add_edge( + glitter_scan_converter_t *converter, + glitter_input_scaled_t x1, glitter_input_scaled_t y1, + glitter_input_scaled_t x2, glitter_input_scaled_t y2, + int dir); + +/* Render the polygon in the scan converter to the given A8 format + * image raster. Only the pixels accessible as pixels[y*stride+x] for + * x,y inside the clip box are written to, where xmin <= x < xmax, + * ymin <= y < ymax. The image is assumed to be clear on input. + * + * If nonzero_fill is true then the interior of the polygon is + * computed with the non-zero fill rule. Otherwise the even-odd fill + * rule is used. + * + * The scan converter must be reset or destroyed after this call. */ +#ifndef GLITTER_BLIT_COVERAGES_ARGS +# define GLITTER_BLIT_COVERAGES_ARGS unsigned char *raster_pixels, long raster_stride +#endif +I glitter_status_t +glitter_scan_converter_render( + glitter_scan_converter_t *converter, + int nonzero_fill, + GLITTER_BLIT_COVERAGES_ARGS); + +/*------------------------------------------------------------------------- + * glitter-paths.c: Implementation internal types + */ +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +/* All polygon coordinates are snapped onto a subsample grid. "Grid + * scaled" numbers are fixed precision reals with multiplier GRID_X or + * GRID_Y. */ +typedef int grid_scaled_t; +typedef int grid_scaled_x_t; +typedef int grid_scaled_y_t; + +/* Default x/y scale factors. + * You can either define GRID_X/Y_BITS to get a power-of-two scale + * or define GRID_X/Y separately. */ +#if !defined(GRID_X) && !defined(GRID_X_BITS) +# define GRID_X_BITS 8 +#endif +#if !defined(GRID_Y) && !defined(GRID_Y_BITS) +# define GRID_Y 15 +#endif + +/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ +#ifdef GRID_X_BITS +# define GRID_X (1 << GRID_X_BITS) +#endif +#ifdef GRID_Y_BITS +# define GRID_Y (1 << GRID_Y_BITS) +#endif + +/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into + * integer and fractional parts. The integer part is floored. */ +#if defined(GRID_X_TO_INT_FRAC) + /* do nothing */ +#elif defined(GRID_X_BITS) +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) +#else +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) +#endif + +#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ + (i) = (t) / (m); \ + (f) = (t) % (m); \ + if ((f) < 0) { \ + --(i); \ + (f) += (m); \ + } \ +} while (0) + +#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ + (f) = (t) & ((1 << (b)) - 1); \ + (i) = (t) >> (b); \ +} while (0) + +/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want + * to be able to represent exactly areas of subpixel trapezoids whose + * vertices are given in grid scaled coordinates. The scale factor + * comes from needing to accurately represent the area 0.5*dx*dy of a + * triangle with base dx and height dy in grid scaled numbers. */ +typedef int grid_area_t; +#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ + +/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ +#if GRID_XY == 510 +# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) +#elif GRID_XY == 255 +# define GRID_AREA_TO_ALPHA(c) (c) +#elif GRID_XY == 64 +# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) +#elif GRID_XY == 128 +# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) +#elif GRID_XY == 256 +# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) +#elif GRID_XY == 15 +# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) +#elif GRID_XY == 2*256*15 +# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4)) >> 9) +#else +# define GRID_AREA_TO_ALPHA(c) ((c)*255 / GRID_XY) /* tweak me for rounding */ +#endif + +#define UNROLL3(x) x x x + +struct quorem { + int quo; + int rem; +}; + +/* Header for a chunk of memory in a memory pool. */ +struct _pool_chunk { + /* # bytes used in this chunk. */ + size_t size; + + /* # bytes total in this chunk */ + size_t capacity; + + /* Pointer to the previous chunk or %NULL if this is the sentinel + * chunk in the pool header. */ + struct _pool_chunk *prev_chunk; + + /* Actual data starts here. Well aligned for pointers. */ +}; + +/* A memory pool. This is supposed to be embedded on the stack or + * within some other structure. It may optionally be followed by an + * embedded array from which requests are fulfilled until + * malloc needs to be called to allocate a first real chunk. */ +struct pool { + /* Chunk we're allocating from. */ + struct _pool_chunk *current; + + /* Free list of previously allocated chunks. All have >= default + * capacity. */ + struct _pool_chunk *first_free; + + /* The default capacity of a chunk. */ + size_t default_capacity; + + /* Header for the sentinel chunk. Directly following the pool + * struct should be some space for embedded elements from which + * the sentinel chunk allocates from. */ + struct _pool_chunk sentinel[1]; +}; + +/* A polygon edge. */ +struct edge { + /* Next in y-bucket or active list. */ + struct edge *next; + + /* Current x coordinate while the edge is on the active + * list. Initialised to the x coordinate of the top of the + * edge. The quotient is in grid_scaled_x_t units and the + * remainder is mod dy in grid_scaled_y_t units.*/ + struct quorem x; + + /* Advance of the current x when moving down a subsample line. */ + struct quorem dxdy; + + /* Advance of the current x when moving down a full pixel + * row. Only initialised when the height of the edge is large + * enough that there's a chance the edge could be stepped by a + * full row's worth of subsample rows at a time. */ + struct quorem dxdy_full; + + /* The clipped y of the top of the edge. */ + grid_scaled_y_t ytop; + + /* y2-y1 after orienting the edge downwards. */ + grid_scaled_y_t dy; + + /* Number of subsample rows remaining to scan convert of this + * edge. */ + grid_scaled_y_t height_left; + + /* Original sign of the edge: +1 for downwards, -1 for upwards + * edges. */ + int dir; +}; + +/* Number of subsample rows per y-bucket. Must be GRID_Y. */ +#define EDGE_Y_BUCKET_HEIGHT GRID_Y + +#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) + +/* A collection of sorted and vertically clipped edges of the polygon. + * Edges are moved from the polygon to an active list while scan + * converting. */ +struct polygon { + /* The vertical clip extents. */ + grid_scaled_y_t ymin, ymax; + + /* Array of edges all starting in the same bucket. An edge is put + * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when + * it is added to the polygon. */ + struct edge **y_buckets; + struct edge *y_buckets_embedded[64]; + + struct { + struct pool base[1]; + struct edge embedded[32]; + } edge_pool; +}; + +/* A cell records the effect on pixel coverage of polygon edges + * passing through a pixel. It contains two accumulators of pixel + * coverage. + * + * Consider the effects of a polygon edge on the coverage of a pixel + * it intersects and that of the following one. The coverage of the + * following pixel is the height of the edge multiplied by the width + * of the pixel, and the coverage of the pixel itself is the area of + * the trapezoid formed by the edge and the right side of the pixel. + * + * +-----------------------+-----------------------+ + * | | | + * | | | + * |_______________________|_______________________| + * | \...................|.......................|\ + * | \..................|.......................| | + * | \.................|.......................| | + * | \....covered.....|.......................| | + * | \....area.......|.......................| } covered height + * | \..............|.......................| | + * |uncovered\.............|.......................| | + * | area \............|.......................| | + * |___________\...........|.......................|/ + * | | | + * | | | + * | | | + * +-----------------------+-----------------------+ + * + * Since the coverage of the following pixel will always be a multiple + * of the width of the pixel, we can store the height of the covered + * area instead. The coverage of the pixel itself is the total + * coverage minus the area of the uncovered area to the left of the + * edge. As it's faster to compute the uncovered area we only store + * that and subtract it from the total coverage later when forming + * spans to blit. + * + * The heights and areas are signed, with left edges of the polygon + * having positive sign and right edges having negative sign. When + * two edges intersect they swap their left/rightness so their + * contribution above and below the intersection point must be + * computed separately. */ +struct cell { + struct cell *next; + int x; + grid_area_t uncovered_area; + grid_scaled_y_t covered_height; +}; + +/* A cell list represents the scan line sparsely as cells ordered by + * ascending x. It is geared towards scanning the cells in order + * using an internal cursor. */ +struct cell_list { + /* Points to the left-most cell in the scan line. */ + struct cell *head; + /* Sentinel node */ + struct cell tail; + + /* Cursor state for iterating through the cell list. Points to + * a pointer to the current cell: either &cell_list->head or the next + * field of the previous cell. */ + struct cell **cursor; + + /* Cells in the cell list are owned by the cell list and are + * allocated from this pool. */ + struct { + struct pool base[1]; + struct cell embedded[32]; + } cell_pool; +}; + +struct cell_pair { + struct cell *cell1; + struct cell *cell2; +}; + +/* The active list contains edges in the current scan line ordered by + * the x-coordinate of the intercept of the edge and the scan line. */ +struct active_list { + /* Leftmost edge on the current scan line. */ + struct edge *head; + + /* A lower bound on the height of the active edges is used to + * estimate how soon some active edge ends. We can't advance the + * scan conversion by a full pixel row if an edge ends somewhere + * within it. */ + grid_scaled_y_t min_height; +}; + +struct glitter_scan_converter { + struct polygon polygon[1]; + struct active_list active[1]; + struct cell_list coverages[1]; + + /* Clip box. */ + grid_scaled_x_t xmin, xmax; + grid_scaled_y_t ymin, ymax; +}; + +/* Compute the floored division a/b. Assumes / and % perform symmetric + * division. */ +inline static struct quorem +floored_divrem(int a, int b) +{ + struct quorem qr; + qr.quo = a/b; + qr.rem = a%b; + if ((a^b)<0 && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric + * division. */ +static struct quorem +floored_muldivrem(int x, int a, int b) +{ + struct quorem qr; + long long xa = (long long)x*a; + qr.quo = xa/b; + qr.rem = xa%b; + if ((xa>=0) != (b>=0) && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +static void +_pool_chunk_init( + struct _pool_chunk *p, + struct _pool_chunk *prev_chunk, + size_t capacity) +{ + p->prev_chunk = prev_chunk; + p->size = 0; + p->capacity = capacity; +} + +static struct _pool_chunk * +_pool_chunk_create( + struct _pool_chunk *prev_chunk, + size_t size) +{ + struct _pool_chunk *p; + size_t size_with_head = size + sizeof(struct _pool_chunk); + if (size_with_head < size) + return NULL; + p = malloc(size_with_head); + if (p) + _pool_chunk_init(p, prev_chunk, size); + return p; +} + +static void +pool_init( + struct pool *pool, + size_t default_capacity, + size_t embedded_capacity) +{ + pool->current = pool->sentinel; + pool->first_free = NULL; + pool->default_capacity = default_capacity; + _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); +} + +static void +pool_fini(struct pool *pool) +{ + struct _pool_chunk *p = pool->current; + do { + while (NULL != p) { + struct _pool_chunk *prev = p->prev_chunk; + if (p != pool->sentinel) + free(p); + p = prev; + } + p = pool->first_free; + pool->first_free = NULL; + } while (NULL != p); + pool_init(pool, 0, 0); +} + +/* Satisfy an allocation by first allocating a new large enough chunk + * and adding it to the head of the pool's chunk list. This function + * is called as a fallback if pool_alloc() couldn't do a quick + * allocation from the current chunk in the pool. */ +static void * +_pool_alloc_from_new_chunk( + struct pool *pool, + size_t size) +{ + struct _pool_chunk *chunk; + void *obj; + size_t capacity; + + /* If the allocation is smaller than the default chunk size then + * try getting a chunk off the free list. Force alloc of a new + * chunk for large requests. */ + capacity = size; + chunk = NULL; + if (size < pool->default_capacity) { + capacity = pool->default_capacity; + chunk = pool->first_free; + if (chunk) { + pool->first_free = chunk->prev_chunk; + _pool_chunk_init(chunk, pool->current, chunk->capacity); + } + } + + if (NULL == chunk) { + chunk = _pool_chunk_create( + pool->current, + capacity); + if (NULL == chunk) + return NULL; + } + pool->current = chunk; + + obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; +} + +/* Allocate size bytes from the pool. The first allocated address + * returned from a pool is aligned to sizeof(void*). Subsequent + * addresses will maintain alignment as long as multiples of void* are + * allocated. Returns the address of a new memory area or %NULL on + * allocation failures. The pool retains ownership of the returned + * memory. */ +inline static void * +pool_alloc( + struct pool *pool, + size_t size) +{ + struct _pool_chunk *chunk = pool->current; + + if (size <= chunk->capacity - chunk->size) { + void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; + } + else { + return _pool_alloc_from_new_chunk(pool, size); + } +} + +/* Relinquish all pool_alloced memory back to the pool. */ +static void +pool_reset(struct pool *pool) +{ + /* Transfer all used chunks to the chunk free list. */ + struct _pool_chunk *chunk = pool->current; + if (chunk != pool->sentinel) { + while (chunk->prev_chunk != pool->sentinel) { + chunk = chunk->prev_chunk; + } + chunk->prev_chunk = pool->first_free; + pool->first_free = pool->current; + } + /* Reset the sentinel as the current chunk. */ + pool->current = pool->sentinel; + pool->sentinel->size = 0; +} + +/* Rewinds the cell list's cursor to the beginning. After rewinding + * we're good to cell_list_find() the cell any x coordinate. */ +inline static void +cell_list_rewind(struct cell_list *cells) +{ + cells->cursor = &cells->head; +} + +/* Rewind the cell list if its cursor has been advanced past x. */ +inline static void +cell_list_maybe_rewind(struct cell_list *cells, int x) +{ + struct cell *tail = *cells->cursor; + if (tail->x > x) { + cell_list_rewind(cells); + } +} + +static void +cell_list_init(struct cell_list *cells) +{ + pool_init(cells->cell_pool.base, + 256*sizeof(struct cell), + sizeof(cells->cell_pool.embedded)); + cells->tail.next = NULL; + cells->tail.x = INT_MAX; + cells->head = &cells->tail; + cell_list_rewind(cells); +} + +static void +cell_list_fini(struct cell_list *cells) +{ + pool_fini(cells->cell_pool.base); + cell_list_init(cells); +} + +/* Empty the cell list. This is called at the start of every pixel + * row. */ +inline static void +cell_list_reset(struct cell_list *cells) +{ + cell_list_rewind(cells); + cells->head = &cells->tail; + pool_reset(cells->cell_pool.base); +} + +/* Find a cell at the given x-coordinate. Returns %NULL if a new cell + * needed to be allocated but couldn't be. Cells must be found with + * non-decreasing x-coordinate until the cell list is rewound using + * cell_list_rewind(). Ownership of the returned cell is retained by + * the cell list. */ +inline static struct cell * +cell_list_find(struct cell_list *cells, int x) +{ + struct cell **cursor = cells->cursor; + struct cell *tail; + + while (1) { + UNROLL3({ + tail = *cursor; + if (tail->x >= x) { + break; + } + cursor = &tail->next; + }); + } + cells->cursor = cursor; + + if (tail->x == x) { + return tail; + } else { + struct cell *cell = pool_alloc( + cells->cell_pool.base, + sizeof(struct cell)); + if (NULL == cell) + return NULL; + *cursor = cell; + cell->next = tail; + cell->x = x; + cell->uncovered_area = 0; + cell->covered_height = 0; + return cell; + } +} + +/* Find two cells at x1 and x2. This is exactly equivalent + * to + * + * pair.cell1 = cell_list_find(cells, x1); + * pair.cell2 = cell_list_find(cells, x2); + * + * except with less function call overhead. */ +inline static struct cell_pair +cell_list_find_pair(struct cell_list *cells, int x1, int x2) +{ + struct cell_pair pair; + struct cell **cursor = cells->cursor; + struct cell *cell1; + struct cell *cell2; + struct cell *newcell; + + /* Find first cell at x1. */ + while (1) { + UNROLL3({ + cell1 = *cursor; + if (cell1->x > x1) + break; + if (cell1->x == x1) + goto found_first; + cursor = &cell1->next; + }); + } + + /* New first cell at x1. */ + newcell = pool_alloc( + cells->cell_pool.base, + sizeof(struct cell)); + if (NULL != newcell) { + *cursor = newcell; + newcell->next = cell1; + newcell->x = x1; + newcell->uncovered_area = 0; + newcell->covered_height = 0; + } + cell1 = newcell; + found_first: + + /* Find second cell at x2. */ + while (1) { + UNROLL3({ + cell2 = *cursor; + if (cell2->x > x2) + break; + if (cell2->x == x2) + goto found_second; + cursor = &cell2->next; + }); + } + + /* New second cell at x2. */ + newcell = pool_alloc( + cells->cell_pool.base, + sizeof(struct cell)); + if (NULL != newcell) { + *cursor = newcell; + newcell->next = cell2; + newcell->x = x2; + newcell->uncovered_area = 0; + newcell->covered_height = 0; + } + cell2 = newcell; + found_second: + + cells->cursor = cursor; + pair.cell1 = cell1; + pair.cell2 = cell2; + return pair; +} + +/* Add an unbounded subpixel span covering subpixels >= x to the + * coverage cells. */ +static glitter_status_t +cell_list_add_unbounded_subspan( + struct cell_list *cells, + grid_scaled_x_t x) +{ + struct cell *cell; + int ix, fx; + + GRID_X_TO_INT_FRAC(x, ix, fx); + + cell = cell_list_find(cells, ix); + if (cell) { + cell->uncovered_area += 2*fx; + cell->covered_height++; + return GLITTER_STATUS_SUCCESS; + } + return GLITTER_STATUS_NO_MEMORY; +} + +/* Add a subpixel span covering [x1, x2) to the coverage cells. */ +inline static glitter_status_t +cell_list_add_subspan( + struct cell_list *cells, + grid_scaled_x_t x1, + grid_scaled_x_t x2) +{ + int ix1, fx1; + int ix2, fx2; + + GRID_X_TO_INT_FRAC(x1, ix1, fx1); + GRID_X_TO_INT_FRAC(x2, ix2, fx2); + + if (ix1 != ix2) { + struct cell_pair p; + p = cell_list_find_pair(cells, ix1, ix2); + if (p.cell1 && p.cell2) { + p.cell1->uncovered_area += 2*fx1; + ++p.cell1->covered_height; + p.cell2->uncovered_area -= 2*fx2; + --p.cell2->covered_height; + return GLITTER_STATUS_SUCCESS; + } + } + else { + struct cell *cell = cell_list_find(cells, ix1); + if (cell) { + cell->uncovered_area += 2*(fx1-fx2); + return GLITTER_STATUS_SUCCESS; + } + } + return GLITTER_STATUS_NO_MEMORY; +} + +/* Adds the analytical coverage of an edge crossing the current pixel + * row to the coverage cells and advances the edge's x position to the + * following row. + * + * This function is only called when we know that during this pixel row: + * + * 1) The relative order of all edges on the active list doesn't + * change. In particular, no edges intersect within this row to pixel + * precision. + * + * 2) No new edges start in this row. + * + * 3) No existing edges end mid-row. + * + * This function depends on being called with all edges from the + * active list in the order they appear on the list (i.e. with + * non-decreasing x-coordinate.) */ +static glitter_status_t +cell_list_render_edge( + struct cell_list *cells, + struct edge *edge, + int sign) +{ + struct quorem x1 = edge->x; + struct quorem x2 = x1; + grid_scaled_y_t y1, y2, dy; + grid_scaled_x_t dx; + int ix1, ix2; + grid_scaled_x_t fx1, fx2; + + x2.quo += edge->dxdy_full.quo; + x2.rem += edge->dxdy_full.rem; + if (x2.rem >= 0) { + ++x2.quo; + x2.rem -= edge->dy; + } + edge->x = x2; + + GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); + GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); + + /* Edge is entirely within a column? */ + if (ix1 == ix2) { + /* We always know that ix1 is >= the cell list cursor in this + * case due to the no-intersections precondition. */ + struct cell *cell = cell_list_find(cells, ix1); + if (NULL == cell) + return GLITTER_STATUS_NO_MEMORY; + cell->covered_height += sign*GRID_Y; + cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; + return GLITTER_STATUS_SUCCESS; + } + + /* Orient the edge left-to-right. */ + dx = x2.quo - x1.quo; + if (dx >= 0) { + y1 = 0; + y2 = GRID_Y; + } else { + int tmp; + tmp = ix1; ix1 = ix2; ix2 = tmp; + tmp = fx1; fx1 = fx2; fx2 = tmp; + dx = -dx; + sign = -sign; + y1 = GRID_Y; + y2 = 0; + } + dy = y2 - y1; + + /* Add coverage for all pixels [ix1,ix2] on this row crossed + * by the edge. */ + { + struct cell_pair pair; + struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); + + /* When rendering a previous edge on the active list we may + * advance the cell list cursor past the leftmost pixel of the + * current edge even though the two edges don't intersect. + * e.g. consider two edges going down and rightwards: + * + * --\_+---\_+-----+-----+---- + * \_ \_ | | + * | \_ | \_ | | + * | \_| \_| | + * | \_ \_ | + * ----+-----+-\---+-\---+---- + * + * The left edge touches cells past the starting cell of the + * right edge. Fortunately such cases are rare. + * + * The rewinding is never necessary if the current edge stays + * within a single column because we've checked before calling + * this function that the active list order won't change. */ + cell_list_maybe_rewind(cells, ix1); + + pair = cell_list_find_pair(cells, ix1, ix1+1); + if (!pair.cell1 || !pair.cell2) + return GLITTER_STATUS_NO_MEMORY; + + pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); + pair.cell1->covered_height += sign*y.quo; + y.quo += y1; + + if (ix1+1 < ix2) { + struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); + struct cell *cell = pair.cell2; + + ++ix1; + do { + grid_scaled_y_t y_skip = dydx_full.quo; + y.rem += dydx_full.rem; + if (y.rem >= dx) { + ++y_skip; + y.rem -= dx; + } + + y.quo += y_skip; + + y_skip *= sign; + cell->uncovered_area += y_skip*GRID_X; + cell->covered_height += y_skip; + + ++ix1; + cell = cell_list_find(cells, ix1); + if (NULL == cell) + return GLITTER_STATUS_NO_MEMORY; + } while (ix1 != ix2); + + pair.cell2 = cell; + } + pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; + pair.cell2->covered_height += sign*(y2 - y.quo); + } + + return GLITTER_STATUS_SUCCESS; +} + +static void +polygon_init(struct polygon *polygon) +{ + polygon->ymin = polygon->ymax = 0; + polygon->y_buckets = polygon->y_buckets_embedded; + pool_init(polygon->edge_pool.base, + 8192 - sizeof(struct _pool_chunk), + sizeof(polygon->edge_pool.embedded)); +} + +static void +polygon_fini(struct polygon *polygon) +{ + if (polygon->y_buckets != polygon->y_buckets_embedded) + free(polygon->y_buckets); + pool_fini(polygon->edge_pool.base); + polygon_init(polygon); +} + +/* Empties the polygon of all edges. The polygon is then prepared to + * receive new edges and clip them to the vertical range + * [ymin,ymax). */ +static glitter_status_t +polygon_reset( + struct polygon *polygon, + grid_scaled_y_t ymin, + grid_scaled_y_t ymax) +{ + unsigned h = ymax - ymin; + unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, + ymin); + + pool_reset(polygon->edge_pool.base); + + if (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT) + goto bail_no_mem; /* even if you could, you wouldn't want to. */ + + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + polygon->y_buckets = polygon->y_buckets_embedded; + if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { + polygon->y_buckets = _cairo_malloc_ab (num_buckets, + sizeof (struct edge *)); + if (unlikely (NULL == polygon->y_buckets)) + goto bail_no_mem; + } + memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); + + polygon->ymin = ymin; + polygon->ymax = ymax; + return GLITTER_STATUS_SUCCESS; + + bail_no_mem: + polygon->ymin = 0; + polygon->ymax = 0; + return GLITTER_STATUS_NO_MEMORY; +} + +static void +_polygon_insert_edge_into_its_y_bucket( + struct polygon *polygon, + struct edge *e) +{ + unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); + struct edge **ptail = &polygon->y_buckets[ix]; + e->next = *ptail; + *ptail = e; +} + +inline static glitter_status_t +polygon_add_edge( + struct polygon *polygon, + int x0, int y0, + int x1, int y1, + int dir) +{ + struct edge *e; + grid_scaled_x_t dx; + grid_scaled_y_t dy; + grid_scaled_y_t ytop, ybot; + grid_scaled_y_t ymin = polygon->ymin; + grid_scaled_y_t ymax = polygon->ymax; + + if (y0 == y1) + return GLITTER_STATUS_SUCCESS; + + if (y0 > y1) { + int tmp; + tmp = x0; x0 = x1; x1 = tmp; + tmp = y0; y0 = y1; y1 = tmp; + dir = -dir; + } + + if (y0 >= ymax || y1 <= ymin) + return GLITTER_STATUS_SUCCESS; + + e = pool_alloc(polygon->edge_pool.base, + sizeof(struct edge)); + if (NULL == e) + return GLITTER_STATUS_NO_MEMORY; + + dx = x1 - x0; + dy = y1 - y0; + e->dy = dy; + e->dxdy = floored_divrem(dx, dy); + + if (ymin <= y0) { + ytop = y0; + e->x.quo = x0; + e->x.rem = 0; + } + else { + ytop = ymin; + e->x = floored_muldivrem(ymin - y0, dx, dy); + e->x.quo += x0; + } + + e->dir = dir; + e->ytop = ytop; + ybot = y1 < ymax ? y1 : ymax; + e->height_left = ybot - ytop; + + if (e->height_left >= GRID_Y) { + e->dxdy_full = floored_muldivrem(GRID_Y, dx, dy); + } + else { + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } + + _polygon_insert_edge_into_its_y_bucket(polygon, e); + + e->x.rem -= dy; /* Bias the remainder for faster + * edge advancement. */ + return GLITTER_STATUS_SUCCESS; +} + +static void +active_list_reset( + struct active_list *active) +{ + active->head = NULL; + active->min_height = 0; +} + +static void +active_list_init(struct active_list *active) +{ + active_list_reset(active); +} + +static void +active_list_fini( + struct active_list *active) +{ + active_list_reset(active); +} + +/* Merge the edges in an unsorted list of edges into a sorted + * list. The sort order is edges ascending by edge->x.quo. Returns + * the new head of the sorted list. */ +static struct edge * +merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head) +{ + struct edge *head = unsorted_head; + struct edge **cursor = &sorted_head; + int x; + + while (NULL != head) { + struct edge *prev = *cursor; + struct edge *next = head->next; + x = head->x.quo; + + if (NULL == prev || x < prev->x.quo) { + cursor = &sorted_head; + } + + while (1) { + UNROLL3({ + prev = *cursor; + if (NULL == prev || prev->x.quo >= x) + break; + cursor = &prev->next; + }); + } + + head->next = *cursor; + *cursor = head; + + head = next; + } + return sorted_head; +} + +/* Test if the edges on the active list can be safely advanced by a + * full row without intersections or any edges ending. */ +inline static int +active_list_can_step_full_row( + struct active_list *active) +{ + /* Recomputes the minimum height of all edges on the active + * list if we have been dropping edges. */ + if (active->min_height <= 0) { + struct edge *e = active->head; + int min_height = INT_MAX; + + while (NULL != e) { + if (e->height_left < min_height) + min_height = e->height_left; + e = e->next; + } + + active->min_height = min_height; + } + + /* Check for intersections only if no edges end during the next + * row. */ + if (active->min_height >= GRID_Y) { + grid_scaled_x_t prev_x = INT_MIN; + struct edge *e = active->head; + while (NULL != e) { + struct quorem x = e->x; + + x.quo += e->dxdy_full.quo; + x.rem += e->dxdy_full.rem; + if (x.rem >= 0) + ++x.quo; + + if (x.quo <= prev_x) + return 0; + prev_x = x.quo; + e = e->next; + } + return 1; + } + return 0; +} + +/* Merges edges on the given subpixel row from the polygon to the + * active_list. */ +inline static void +active_list_merge_edges_from_polygon( + struct active_list *active, + grid_scaled_y_t y, + struct polygon *polygon) +{ + /* Split off the edges on the current subrow and merge them into + * the active list. */ + unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin); + int min_height = active->min_height; + struct edge *subrow_edges = NULL; + struct edge **ptail = &polygon->y_buckets[ix]; + + while (1) { + struct edge *tail = *ptail; + if (NULL == tail) break; + + if (y == tail->ytop) { + *ptail = tail->next; + tail->next = subrow_edges; + subrow_edges = tail; + if (tail->height_left < min_height) + min_height = tail->height_left; + } + else { + ptail = &tail->next; + } + } + active->head = merge_unsorted_edges(active->head, subrow_edges); + active->min_height = min_height; +} + +/* Advance the edges on the active list by one subsample row by + * updating their x positions. Drop edges from the list that end. */ +inline static void +active_list_substep_edges( + struct active_list *active) +{ + struct edge **cursor = &active->head; + grid_scaled_x_t prev_x = INT_MIN; + struct edge *unsorted = NULL; + + while (1) { + struct edge *edge; + + UNROLL3({ + edge = *cursor; + if (NULL == edge) + break; + + if (0 != --edge->height_left) { + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } + + if (edge->x.quo < prev_x) { + *cursor = edge->next; + edge->next = unsorted; + unsorted = edge; + } else { + prev_x = edge->x.quo; + cursor = &edge->next; + } + + } else { + *cursor = edge->next; + } + }); + } + + if (unsorted) + active->head = merge_unsorted_edges(active->head, unsorted); +} + +inline static glitter_status_t +apply_nonzero_fill_rule_for_subrow( + struct active_list *active, + struct cell_list *coverages) +{ + struct edge *edge = active->head; + int winding = 0; + int xstart; + int xend; + int status; + + cell_list_rewind(coverages); + + while (NULL != edge) { + xstart = edge->x.quo; + winding = edge->dir; + while (1) { + edge = edge->next; + if (NULL == edge) { + return cell_list_add_unbounded_subspan( + coverages, xstart); + } + winding += edge->dir; + if (0 == winding) + break; + } + + xend = edge->x.quo; + status = cell_list_add_subspan(coverages, xstart, xend); + if (status) + return status; + + edge = edge->next; + } + + return GLITTER_STATUS_SUCCESS; +} + +static glitter_status_t +apply_evenodd_fill_rule_for_subrow( + struct active_list *active, + struct cell_list *coverages) +{ + struct edge *edge = active->head; + int xstart; + int xend; + int status; + + cell_list_rewind(coverages); + + while (NULL != edge) { + xstart = edge->x.quo; + + edge = edge->next; + if (NULL == edge) { + return cell_list_add_unbounded_subspan( + coverages, xstart); + } + + xend = edge->x.quo; + status = cell_list_add_subspan(coverages, xstart, xend); + if (status) + return status; + + edge = edge->next; + } + + return GLITTER_STATUS_SUCCESS; +} + +static glitter_status_t +apply_nonzero_fill_rule_and_step_edges( + struct active_list *active, + struct cell_list *coverages) +{ + struct edge **cursor = &active->head; + struct edge *left_edge; + int status; + + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; + int winding = left_edge->dir; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) { + cursor = &left_edge->next; + } + else { + *cursor = left_edge->next; + } + + while (1) { + right_edge = *cursor; + + if (NULL == right_edge) { + return cell_list_render_edge( + coverages, left_edge, +1); + } + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) { + cursor = &right_edge->next; + } + else { + *cursor = right_edge->next; + } + + winding += right_edge->dir; + if (0 == winding) + break; + + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } + } + + status = cell_list_render_edge( + coverages, left_edge, +1); + if (status) + return status; + status = cell_list_render_edge( + coverages, right_edge, -1); + if (status) + return status; + + left_edge = *cursor; + } + + return GLITTER_STATUS_SUCCESS; +} + +static glitter_status_t +apply_evenodd_fill_rule_and_step_edges( + struct active_list *active, + struct cell_list *coverages) +{ + struct edge **cursor = &active->head; + struct edge *left_edge; + int status; + + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) { + cursor = &left_edge->next; + } + else { + *cursor = left_edge->next; + } + + right_edge = *cursor; + + if (NULL == right_edge) { + return cell_list_render_edge( + coverages, left_edge, +1); + } + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) { + cursor = &right_edge->next; + } + else { + *cursor = right_edge->next; + } + + status = cell_list_render_edge( + coverages, left_edge, +1); + if (status) + return status; + status = cell_list_render_edge( + coverages, right_edge, -1); + if (status) + return status; + + left_edge = *cursor; + } + + return GLITTER_STATUS_SUCCESS; +} + +/* If the user hasn't configured a coverage blitter, use a default one + * that blits spans directly to an A8 raster. */ +#ifndef GLITTER_BLIT_COVERAGES + +inline static void +blit_span( + unsigned char *row_pixels, + int x, unsigned len, + grid_area_t coverage) +{ + int alpha = GRID_AREA_TO_ALPHA(coverage); + if (1 == len) { + row_pixels[x] = alpha; + } + else { + memset(row_pixels + x, alpha, len); + } +} + +#define GLITTER_BLIT_COVERAGES(coverages, y, xmin, xmax) \ + blit_cells(coverages, raster_pixels + (y)*raster_stride, xmin, xmax) + +static void +blit_cells( + struct cell_list *cells, + unsigned char *row_pixels, + int xmin, int xmax) +{ + struct cell *cell = cells->head; + int prev_x = xmin; + int coverage = 0; + if (NULL == cell) + return; + + while (NULL != cell && cell->x < xmin) { + coverage += cell->covered_height; + cell = cell->next; + } + coverage *= GRID_X*2; + + for (; NULL != cell; cell = cell->next) { + int x = cell->x; + int area; + if (x >= xmax) + break; + if (x > prev_x && 0 != coverage) { + blit_span(row_pixels, prev_x, x - prev_x, coverage); + } + + coverage += cell->covered_height * GRID_X*2; + area = coverage - cell->uncovered_area; + if (area) { + blit_span(row_pixels, x, 1, area); + } + prev_x = x+1; + } + + if (0 != coverage && prev_x < xmax) { + blit_span(row_pixels, prev_x, xmax - prev_x, coverage); + } +} +#endif /* GLITTER_BLIT_COVERAGES */ + +static void +_glitter_scan_converter_init(glitter_scan_converter_t *converter) +{ + polygon_init(converter->polygon); + active_list_init(converter->active); + cell_list_init(converter->coverages); + converter->xmin=0; + converter->ymin=0; + converter->xmax=0; + converter->ymax=0; +} + +static void +_glitter_scan_converter_fini(glitter_scan_converter_t *converter) +{ + polygon_fini(converter->polygon); + active_list_fini(converter->active); + cell_list_fini(converter->coverages); + converter->xmin=0; + converter->ymin=0; + converter->xmax=0; + converter->ymax=0; +} + +static grid_scaled_t +int_to_grid_scaled(int i, int scale) +{ + /* Clamp to max/min representable scaled number. */ + if (i >= 0) { + if (i >= INT_MAX/scale) + i = INT_MAX/scale; + } + else { + if (i <= INT_MIN/scale) + i = INT_MIN/scale; + } + return i*scale; +} + +#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) +#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) + +I glitter_status_t +glitter_scan_converter_reset( + glitter_scan_converter_t *converter, + int xmin, int ymin, + int xmax, int ymax) +{ + glitter_status_t status; + + converter->xmin = 0; converter->xmax = 0; + converter->ymin = 0; converter->ymax = 0; + + xmin = int_to_grid_scaled_x(xmin); + ymin = int_to_grid_scaled_y(ymin); + xmax = int_to_grid_scaled_x(xmax); + ymax = int_to_grid_scaled_y(ymax); + + active_list_reset(converter->active); + cell_list_reset(converter->coverages); + status = polygon_reset(converter->polygon, ymin, ymax); + if (status) + return status; + + converter->xmin = xmin; + converter->xmax = xmax; + converter->ymin = ymin; + converter->ymax = ymax; + return GLITTER_STATUS_SUCCESS; +} + +/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) + * These macros convert an input coordinate in the client's + * device space to the rasterisation grid. + */ +/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use + * shifts if possible, and something saneish if not. + */ +#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) +#else +# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) +#endif + +#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) +#else +# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) +#endif + +#define INPUT_TO_GRID_general(in, out, grid_scale) do { \ + long long tmp__ = (long long)(grid_scale) * (in); \ + tmp__ >>= GLITTER_INPUT_BITS; \ + (out) = tmp__; \ +} while (0) + +I glitter_status_t +glitter_scan_converter_add_edge( + glitter_scan_converter_t *converter, + glitter_input_scaled_t x1, glitter_input_scaled_t y1, + glitter_input_scaled_t x2, glitter_input_scaled_t y2, + int dir) +{ + /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ + grid_scaled_y_t sx1, sy1; + grid_scaled_y_t sx2, sy2; + + INPUT_TO_GRID_Y(y1, sy1); + INPUT_TO_GRID_Y(y2, sy2); + if (sy1 == sy2) + return GLITTER_STATUS_SUCCESS; + + INPUT_TO_GRID_X(x1, sx1); + INPUT_TO_GRID_X(x2, sx2); + + return polygon_add_edge( + converter->polygon, sx1, sy1, sx2, sy2, dir); +} + +#ifndef GLITTER_BLIT_COVERAGES_BEGIN +# define GLITTER_BLIT_COVERAGES_BEGIN +#endif + +#ifndef GLITTER_BLIT_COVERAGES_END +# define GLITTER_BLIT_COVERAGES_END +#endif + +#ifndef GLITTER_BLIT_COVERAGES_EMPTY +# define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) +#endif + +I glitter_status_t +glitter_scan_converter_render( + glitter_scan_converter_t *converter, + int nonzero_fill, + GLITTER_BLIT_COVERAGES_ARGS) +{ + int i; + int ymax_i = converter->ymax / GRID_Y; + int ymin_i = converter->ymin / GRID_Y; + int xmin_i, xmax_i; + int h = ymax_i - ymin_i; + struct polygon *polygon = converter->polygon; + struct cell_list *coverages = converter->coverages; + struct active_list *active = converter->active; + + xmin_i = converter->xmin / GRID_X; + xmax_i = converter->xmax / GRID_X; + if (xmin_i >= xmax_i) + return GLITTER_STATUS_SUCCESS; + + /* Let the coverage blitter initialise itself. */ + GLITTER_BLIT_COVERAGES_BEGIN; + + /* Render each pixel row. */ + for (i=0; i<h; i++) { + int do_full_step = 0; + glitter_status_t status = 0; + + /* Determine if we can ignore this row or use the full pixel + * stepper. */ + if (GRID_Y == EDGE_Y_BUCKET_HEIGHT + && !polygon->y_buckets[i]) + { + if (!active->head) { + GLITTER_BLIT_COVERAGES_EMPTY(i+ymin_i, xmin_i, xmax_i); + continue; + } + do_full_step = active_list_can_step_full_row(active); + } + + cell_list_reset(coverages); + + if (do_full_step) { + /* Step by a full pixel row's worth. */ + if (nonzero_fill) { + status = apply_nonzero_fill_rule_and_step_edges( + active, coverages); + } + else { + status = apply_evenodd_fill_rule_and_step_edges( + active, coverages); + } + } + else { + /* Subsample this row. */ + grid_scaled_y_t suby; + for (suby = 0; suby < GRID_Y; suby++) { + grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; + + active_list_merge_edges_from_polygon( + active, y, polygon); + + if (nonzero_fill) + status |= apply_nonzero_fill_rule_for_subrow( + active, coverages); + else + status |= apply_evenodd_fill_rule_for_subrow( + active, coverages); + + active_list_substep_edges(active); + } + } + + if (status) + return status; + + GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i); + + if (!active->head) { + active->min_height = INT_MAX; + } + else { + active->min_height -= GRID_Y; + } + } + + /* Clean up the coverage blitter. */ + GLITTER_BLIT_COVERAGES_END; + + return GLITTER_STATUS_SUCCESS; +} + +/*------------------------------------------------------------------------- + * cairo specific implementation: the coverage blitter and + * scan converter subclass. */ + +static glitter_status_t +blit_with_span_renderer( + struct cell_list *cells, + cairo_span_renderer_t *renderer, + struct pool *span_pool, + int y, + int xmin, + int xmax) +{ + struct cell *cell = cells->head; + int prev_x = xmin; + int cover = 0; + cairo_half_open_span_t *spans; + unsigned num_spans; + if (cell == NULL) + return CAIRO_STATUS_SUCCESS; + + /* Skip cells to the left of the clip region. */ + while (cell != NULL && cell->x < xmin) { + cover += cell->covered_height; + cell = cell->next; + } + cover *= GRID_X*2; + + /* Count number of cells remaining. */ + { + struct cell *next = cell; + num_spans = 0; + while (next) { + next = next->next; + ++num_spans; + } + num_spans = 2*num_spans + 1; + } + + /* Allocate enough spans for the row. */ + pool_reset (span_pool); + spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); + if (spans == NULL) + return GLITTER_STATUS_NO_MEMORY; + + num_spans = 0; + + /* Form the spans from the coverages and areas. */ + for (; cell != NULL; cell = cell->next) { + int x = cell->x; + int area; + if (x >= xmax) + break; + + if (x > prev_x) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + ++num_spans; + } + + cover += cell->covered_height*GRID_X*2; + area = cover - cell->uncovered_area; + + spans[num_spans].x = x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); + ++num_spans; + + prev_x = x+1; + } + + if (prev_x < xmax) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + ++num_spans; + } + + /* Dump them into the renderer. */ + return renderer->render_row (renderer, y, spans, num_spans); +} + +struct _cairo_tor_scan_converter { + cairo_scan_converter_t base; + glitter_scan_converter_t converter[1]; + cairo_fill_rule_t fill_rule; + + struct { + struct pool base[1]; + cairo_half_open_span_t embedded[32]; + } span_pool; +}; + +typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t; + +static void +_cairo_tor_scan_converter_destroy(void *abstract_converter) +{ + cairo_tor_scan_converter_t *self = abstract_converter; + if (self == NULL) { + return; + } + _glitter_scan_converter_fini (self->converter); + pool_fini (self->span_pool.base); + free(self); +} + +static cairo_status_t +_cairo_tor_scan_converter_add_edge( + void *abstract_converter, + cairo_fixed_t x1, + cairo_fixed_t y1, + cairo_fixed_t x2, + cairo_fixed_t y2) +{ + cairo_tor_scan_converter_t *self = abstract_converter; + cairo_status_t status; + status = glitter_scan_converter_add_edge ( + self->converter, + x1, y1, x2, y2, +1); + if (status) { + return _cairo_scan_converter_set_error (self, + _cairo_error (status)); + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tor_scan_converter_generate( + void *abstract_converter, + cairo_span_renderer_t *renderer) +{ + cairo_tor_scan_converter_t *self = abstract_converter; + cairo_status_t status = glitter_scan_converter_render ( + self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING, + renderer, + self->span_pool.base); + if (status) { + return _cairo_scan_converter_set_error (self, + _cairo_error (status)); + } + return CAIRO_STATUS_SUCCESS; +} + +cairo_scan_converter_t * +_cairo_tor_scan_converter_create( + int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_tor_scan_converter_t *self = + calloc (1, sizeof(struct _cairo_tor_scan_converter)); + if (self == NULL) + goto bail_nomem; + + self->base.destroy = &_cairo_tor_scan_converter_destroy; + self->base.add_edge = &_cairo_tor_scan_converter_add_edge; + self->base.generate = &_cairo_tor_scan_converter_generate; + + pool_init (self->span_pool.base, + 250 * sizeof(self->span_pool.embedded[0]), + sizeof(self->span_pool.embedded)); + + _glitter_scan_converter_init (self->converter); + status = glitter_scan_converter_reset ( + self->converter, xmin, ymin, xmax, ymax); + if (status != CAIRO_STATUS_SUCCESS) + goto bail; + + self->fill_rule = fill_rule; + + return &self->base; + + bail: + self->base.destroy(&self->base); + bail_nomem: + return _cairo_scan_converter_create_in_error (CAIRO_STATUS_NO_MEMORY); +} diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c new file mode 100644 index 0000000..a9b600a --- /dev/null +++ b/src/cairo-toy-font-face.c @@ -0,0 +1,524 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005,2008 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth <cworth@cworth.org> + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> + * Behdad Esfahbod <behdad@behdad.org> + */ + +#define _BSD_SOURCE /* for strdup() */ +#include "cairoint.h" + + +static const cairo_font_face_t _cairo_font_face_null_pointer = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_NULL_POINTER, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_string = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_STRING, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_slant = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_SLANT, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_weight = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_WEIGHT, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + + +static const cairo_font_face_backend_t _cairo_toy_font_face_backend; + +static int +_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 + * this mapping is to provide unique #cairo_font_face_t values so that + * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t + * works. Once the corresponding #cairo_font_face_t objects fall out of + * downstream caches, we don't need them in this hash table anymore. + * + * Modifications to this hash table are protected by + * _cairo_toy_font_face_mutex. + */ +static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL; + +static cairo_hash_table_t * +_cairo_toy_font_face_hash_table_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); + + if (cairo_toy_font_face_hash_table == NULL) + { + cairo_toy_font_face_hash_table = + _cairo_hash_table_create (_cairo_toy_font_face_keys_equal); + + if (cairo_toy_font_face_hash_table == NULL) { + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); + return NULL; + } + } + + return cairo_toy_font_face_hash_table; +} + +static void +_cairo_toy_font_face_hash_table_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); +} + +/** + * _cairo_toy_font_face_init_key: + * + * Initialize those portions of #cairo_toy_font_face_t needed to use + * it as a hash table key, including the hash code buried away in + * font_face->base.hash_entry. No memory allocation is performed here + * so that no fini call is needed. We do this to make it easier to use + * an automatic #cairo_toy_font_face_t variable as a key. + **/ +static void +_cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + unsigned long hash; + + key->family = family; + key->owns_family = FALSE; + + key->slant = slant; + key->weight = weight; + + /* 1607 and 1451 are just a couple of arbitrary primes. */ + hash = _cairo_hash_string (family); + hash += ((unsigned long) slant) * 1607; + hash += ((unsigned long) weight) * 1451; + + assert (hash != 0); + key->base.hash_entry.hash = hash; +} + +static cairo_status_t +_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face, + cairo_font_face_t **impl_font_face) +{ + const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (unlikely (font_face->base.status)) + return font_face->base.status; + + if (backend->create_for_toy != NULL && + 0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT, + strlen (CAIRO_USER_FONT_FAMILY_DEFAULT))) + { + status = backend->create_for_toy (font_face, impl_font_face); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + backend = &_cairo_user_font_face_backend; + status = backend->create_for_toy (font_face, impl_font_face); + } + + return status; +} + +static cairo_status_t +_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + char *family_copy; + cairo_status_t status; + + family_copy = strdup (family); + if (unlikely (family_copy == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight); + font_face->owns_family = TRUE; + + _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend); + + status = _cairo_toy_font_face_create_impl_face (font_face, + &font_face->impl_face); + if (unlikely (status)) { + free (family_copy); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) +{ + /* We assert here that we own font_face->family before casting + * away the const qualifer. */ + assert (font_face->owns_family); + free ((char*) font_face->family); + + if (font_face->impl_face) + cairo_font_face_destroy (font_face->impl_face); +} + +static int +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *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 && + face_a->weight == face_b->weight); +} + +/** + * cairo_toy_font_face_create: + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Creates a font face from a triplet of family, slant, and weight. + * These font faces are used in implementation of the the #cairo_t "toy" + * font API. + * + * If @family is the zero-length string "", the platform-specific default + * family is assumed. The default family then can be queried using + * cairo_toy_font_face_get_family(). + * + * The cairo_select_font_face() function uses this to create font faces. + * See that function for limitations and other details of toy font faces. + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.8 + **/ +cairo_font_face_t * +cairo_toy_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + cairo_status_t status; + cairo_toy_font_face_t key, *font_face; + cairo_hash_table_t *hash_table; + + if (family == NULL) + return (cairo_font_face_t*) &_cairo_font_face_null_pointer; + + /* Make sure we've got valid UTF-8 for the family */ + status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL); + if (unlikely (status)) { + if (status == CAIRO_STATUS_INVALID_STRING) + return (cairo_font_face_t*) &_cairo_font_face_invalid_string; + + return (cairo_font_face_t*) &_cairo_font_face_nil; + } + + switch (slant) { + case CAIRO_FONT_SLANT_NORMAL: + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + break; + default: + return (cairo_font_face_t*) &_cairo_font_face_invalid_slant; + } + + switch (weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + case CAIRO_FONT_WEIGHT_BOLD: + break; + default: + return (cairo_font_face_t*) &_cairo_font_face_invalid_weight; + } + + if (*family == '\0') + family = CAIRO_FONT_FAMILY_DEFAULT; + + hash_table = _cairo_toy_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) + goto UNWIND; + + _cairo_toy_font_face_init_key (&key, family, slant, weight); + + /* Return existing font_face if it exists in the hash table. */ + font_face = _cairo_hash_table_lookup (hash_table, + &key.base.hash_entry); + if (font_face != NULL) { + if (font_face->base.status == CAIRO_STATUS_SUCCESS) { + /* We increment the reference count here manually to avoid + double-locking. */ + _cairo_reference_count_inc (&font_face->base.ref_count); + _cairo_toy_font_face_hash_table_unlock (); + return &font_face->base; + } + + /* remove the bad font from the hash table */ + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + font_face->base.hash_entry.hash = 0; + } + + /* Otherwise create it and insert into hash table. */ + font_face = malloc (sizeof (cairo_toy_font_face_t)); + if (unlikely (font_face == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto UNWIND_HASH_TABLE_LOCK; + } + + status = _cairo_toy_font_face_init (font_face, family, slant, weight); + if (unlikely (status)) + goto UNWIND_FONT_FACE_MALLOC; + + assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); + if (unlikely (status)) + goto UNWIND_FONT_FACE_INIT; + + _cairo_toy_font_face_hash_table_unlock (); + + return &font_face->base; + + UNWIND_FONT_FACE_INIT: + _cairo_toy_font_face_fini (font_face); + UNWIND_FONT_FACE_MALLOC: + free (font_face); + UNWIND_HASH_TABLE_LOCK: + _cairo_toy_font_face_hash_table_unlock (); + UNWIND: + return (cairo_font_face_t*) &_cairo_font_face_nil; +} +slim_hidden_def (cairo_toy_font_face_create); + +static void +_cairo_toy_font_face_destroy (void *abstract_face) +{ + cairo_toy_font_face_t *font_face = abstract_face; + cairo_hash_table_t *hash_table; + + if (font_face == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count)) + return; + + hash_table = _cairo_toy_font_face_hash_table_lock (); + /* All created objects must have been mapped in the hash table. */ + assert (hash_table != NULL); + + if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_toy_font_face_hash_table_unlock (); + return; + } + + if (font_face->base.hash_entry.hash != 0) + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + + _cairo_toy_font_face_hash_table_unlock (); + + _cairo_toy_font_face_fini (font_face); +} + +static cairo_status_t +_cairo_toy_font_face_scaled_font_create (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font) +{ + cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face; + + ASSERT_NOT_REACHED; + + return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH); +} + +static cairo_font_face_t * +_cairo_toy_font_face_get_implementation (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_toy_font_face_t *font_face = abstract_font_face; + + if (font_face->impl_face) { + cairo_font_face_t *impl = font_face->impl_face; + + if (impl->backend->get_implementation != NULL) { + return impl->backend->get_implementation (impl, + font_matrix, + ctm, + options); + } + + return cairo_font_face_reference (impl); + } + + return abstract_font_face; +} + +static cairo_bool_t +_cairo_font_face_is_toy (cairo_font_face_t *font_face) +{ + return font_face->backend == &_cairo_toy_font_face_backend; +} + +/** + * cairo_toy_font_face_get_family: + * @font_face: A toy font face + * + * Gets the familly name of a toy font. + * + * Return value: The family name. This string is owned by the font face + * and remains valid as long as the font face is alive (referenced). + * + * Since: 1.8 + **/ +const char * +cairo_toy_font_face_get_family (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_FAMILY_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_FAMILY_DEFAULT; + } + assert (toy_font_face->owns_family); + return toy_font_face->family; +} + +/** + * cairo_toy_font_face_get_slant: + * @font_face: A toy font face + * + * Gets the slant a toy font. + * + * Return value: The slant value + * + * Since: 1.8 + **/ +cairo_font_slant_t +cairo_toy_font_face_get_slant (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_SLANT_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_SLANT_DEFAULT; + } + return toy_font_face->slant; +} +slim_hidden_def (cairo_toy_font_face_get_slant); + +/** + * cairo_toy_font_face_get_weight: + * @font_face: A toy font face + * + * Gets the weight a toy font. + * + * Return value: The weight value + * + * Since: 1.8 + **/ +cairo_font_weight_t +cairo_toy_font_face_get_weight (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_WEIGHT_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_WEIGHT_DEFAULT; + } + return toy_font_face->weight; +} +slim_hidden_def (cairo_toy_font_face_get_weight); + +static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { + CAIRO_FONT_TYPE_TOY, + NULL, /* create_for_toy */ + _cairo_toy_font_face_destroy, + _cairo_toy_font_face_scaled_font_create, + _cairo_toy_font_face_get_implementation +}; + +void +_cairo_toy_font_face_reset_static_data (void) +{ + cairo_hash_table_t *hash_table; + + /* We manually acquire the lock rather than calling + * cairo_toy_font_face_hash_table_lock simply to avoid + * creating the table only to destroy it again. */ + CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); + hash_table = cairo_toy_font_face_hash_table; + cairo_toy_font_face_hash_table = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); + + _cairo_hash_table_destroy (hash_table); +} diff --git a/src/cairo-traps.c b/src/cairo-traps.c index b76e908..092bd9d 100644 --- a/src/cairo-traps.c +++ b/src/cairo-traps.c @@ -47,6 +47,8 @@ _compare_point_fixed_by_y (const void *av, const void *bv); void _cairo_traps_init (cairo_traps_t *traps) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); + traps->status = CAIRO_STATUS_SUCCESS; traps->num_traps = 0; @@ -91,6 +93,8 @@ _cairo_traps_fini (cairo_traps_t *traps) { if (traps->traps != traps->traps_embedded) free (traps->traps); + + VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t))); } /** @@ -131,6 +135,11 @@ _cairo_traps_grow (cairo_traps_t *traps) cairo_trapezoid_t *new_traps; int new_size = 2 * MAX (traps->traps_size, 16); + if (CAIRO_INJECT_FAULT ()) { + traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + if (traps->traps == traps->traps_embedded) { new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t)); if (new_traps != NULL) @@ -140,7 +149,7 @@ _cairo_traps_grow (cairo_traps_t *traps) new_size, sizeof (cairo_trapezoid_t)); } - if (new_traps == NULL) { + if (unlikely (new_traps == NULL)) { traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } @@ -600,7 +609,7 @@ _cairo_traps_extents (const cairo_traps_t *traps, * Determines if a set of trapezoids are exactly representable as a * cairo region. If so, the passed-in region is initialized to * the area representing the given traps. It should be finalized - * with _cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED + * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED * is returned. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED @@ -608,37 +617,35 @@ _cairo_traps_extents (const cairo_traps_t *traps, **/ cairo_int_status_t _cairo_traps_extract_region (const cairo_traps_t *traps, - cairo_region_t *region) + cairo_region_t **region) { - cairo_box_int_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_int_t)]; - cairo_box_int_t *boxes = stack_boxes; - int i, box_count; cairo_int_status_t status; + cairo_region_t *r; + int i; - for (i = 0; i < traps->num_traps; i++) - if (!(traps->traps[i].left.p1.x == traps->traps[i].left.p2.x - && traps->traps[i].right.p1.x == traps->traps[i].right.p2.x - && _cairo_fixed_is_integer(traps->traps[i].top) - && _cairo_fixed_is_integer(traps->traps[i].bottom) - && _cairo_fixed_is_integer(traps->traps[i].left.p1.x) - && _cairo_fixed_is_integer(traps->traps[i].right.p1.x))) { + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { return CAIRO_INT_STATUS_UNSUPPORTED; } - - if (traps->num_traps > ARRAY_LENGTH(stack_boxes)) { - boxes = _cairo_malloc_ab (traps->num_traps, sizeof(cairo_box_int_t)); - - if (boxes == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - box_count = 0; + r = cairo_region_create (); + if (unlikely (r->status)) + return r->status; for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part(traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part(traps->traps[i].top); - int x2 = _cairo_fixed_integer_part(traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part(traps->traps[i].bottom); + cairo_rectangle_int_t rect; + + int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); + int y1 = _cairo_fixed_integer_part (traps->traps[i].top); + int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); + int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); /* XXX: Sometimes we get degenerate trapezoids from the tesellator; * skip these. @@ -646,23 +653,20 @@ _cairo_traps_extract_region (const cairo_traps_t *traps, if (x1 == x2 || y1 == y2) continue; - boxes[box_count].p1.x = x1; - boxes[box_count].p1.y = y1; - boxes[box_count].p2.x = x2; - boxes[box_count].p2.y = y2; + rect.x = x1; + rect.y = y1; + rect.width = x2 - x1; + rect.height = y2 - y1; - box_count++; + status = cairo_region_union_rectangle (r, &rect); + if (unlikely (status)) { + cairo_region_destroy (r); + return status; + } } - status = _cairo_region_init_boxes (region, boxes, box_count); - - if (boxes != stack_boxes) - free (boxes); - - if (status) - _cairo_region_fini (region); - - return status; + *region = r; + return CAIRO_STATUS_SUCCESS; } /* moves trap points such that they become the actual corners of the trapezoid */ @@ -698,15 +702,15 @@ _cairo_traps_path (const cairo_traps_t *traps, _sanitize_trap (&trap); status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top); - if (status) return status; + if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top); - if (status) return status; + if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom); - if (status) return status; + if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom); - if (status) return status; + if (unlikely (status)) return status; status = _cairo_path_fixed_close_path (path); - if (status) return status; + if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; diff --git a/src/cairo-truetype-subset-private.h b/src/cairo-truetype-subset-private.h index 397a9f3..978256f 100644 --- a/src/cairo-truetype-subset-private.h +++ b/src/cairo-truetype-subset-private.h @@ -185,7 +185,7 @@ typedef struct _tt_name { typedef struct _tt_composite_glyph { uint16_t flags; uint16_t index; - uint16_t args[7]; /* 1 to 7 arguments depending on value of flags */ + uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */ } tt_composite_glyph_t; typedef struct _tt_glyph_data { diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c index e8be4b4..92c0547 100644 --- a/src/cairo-truetype-subset.c +++ b/src/cairo-truetype-subset.c @@ -72,7 +72,8 @@ struct _cairo_truetype_font { int num_tables; struct { - char *base_font; + char *font_name; + char *ps_name; unsigned int num_glyphs; int *widths; long x_min, y_min, x_max, y_max; @@ -93,6 +94,21 @@ struct _cairo_truetype_font { }; +/* + * Test that the structs we define for TrueType tables have the + * correct size, ie. they are not padded. + */ +#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S)) +check (tt_head_t, 54); +check (tt_hhea_t, 36); +check (tt_maxp_t, 32); +check (tt_name_record_t, 12); +check (tt_name_t, 18); +check (tt_name_t, 18); +check (tt_composite_glyph_t, 16); +check (tt_glyph_data_t, 26); +#undef check + static cairo_status_t cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, unsigned short glyph, @@ -123,10 +139,7 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, tt_head_t head; tt_hhea_t hhea; tt_maxp_t maxp; - tt_name_t *name; - tt_name_record_t *record; unsigned long size; - int i, j; backend = scaled_font_subset->scaled_font->backend; if (!backend->load_truetype_table) @@ -146,7 +159,7 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, TT_TAG_head, 0, (unsigned char *) &head, &size); - if (status) + if (unlikely (status)) return status; size = sizeof (tt_maxp_t); @@ -154,7 +167,7 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, TT_TAG_maxp, 0, (unsigned char *) &maxp, &size); - if (status) + if (unlikely (status)) return status; size = sizeof (tt_hhea_t); @@ -162,33 +175,12 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); - if (status) + if (unlikely (status)) return status; - size = 0; - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_name, 0, - NULL, - &size); - if (status) - return status; - - name = malloc(size); - if (name == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font_subset->scaled_font, - TT_TAG_name, 0, - (unsigned char *) name, - &size); - if (status) - goto fail0; - font = malloc (sizeof (cairo_truetype_font_t)); - if (font == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail0; - } + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = backend; font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); @@ -198,17 +190,17 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, font->last_boundary = 0; _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); - if (status) + if (unlikely (status)) goto fail1; font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t)); - if (font->glyphs == NULL) { + if (unlikely (font->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int)); - if (font->parent_to_subset == NULL) { + if (unlikely (font->parent_to_subset == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } @@ -224,60 +216,35 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, if (font->base.units_per_em == 0) font->base.units_per_em = 2048; - /* Extract the font name from the name table. At present this - * just looks for the Mac platform/Roman encoded font name. It - * should be extended to use any suitable font name in the - * name table. If the mac/roman font name is not found a - * CairoFont-x-y name is created. - */ - font->base.base_font = NULL; - for (i = 0; i < be16_to_cpu(name->num_records); i++) { - record = &(name->records[i]); - if ((be16_to_cpu (record->platform) == 1) && - (be16_to_cpu (record->encoding) == 0) && - (be16_to_cpu (record->name) == 4)) { - font->base.base_font = malloc (be16_to_cpu(record->length) + 1); - if (font->base.base_font) { - strncpy(font->base.base_font, - ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), - be16_to_cpu (record->length)); - font->base.base_font[be16_to_cpu (record->length)] = 0; - } - break; - } - } - - free (name); - name = NULL; - - if (font->base.base_font == NULL) { - font->base.base_font = malloc (30); - if (font->base.base_font == NULL) { + font->base.font_name = NULL; + status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + &font->base.ps_name, + &font->base.font_name); + if (_cairo_status_is_error (status)) + goto fail3; + + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->base.ps_name == NULL) { + font->base.ps_name = malloc (30); + if (unlikely (font->base.ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } - snprintf(font->base.base_font, 30, "CairoFont-%u-%u", + snprintf(font->base.ps_name, 30, "CairoFont-%u-%u", scaled_font_subset->font_id, scaled_font_subset->subset_id); } - for (i = 0, j = 0; font->base.base_font[j]; j++) { - if (font->base.base_font[j] == ' ') - continue; - font->base.base_font[i++] = font->base.base_font[j]; - } - font->base.base_font[i] = '\0'; - font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int)); - if (font->base.widths == NULL) { + if (unlikely (font->base.widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } _cairo_array_init (&font->string_offsets, sizeof (unsigned long)); status = _cairo_array_grow_by (&font->string_offsets, 10); - if (status) + if (unlikely (status)) goto fail5; font->status = CAIRO_STATUS_SUCCESS; @@ -290,17 +257,16 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, _cairo_array_fini (&font->string_offsets); free (font->base.widths); fail4: - free (font->base.base_font); + free (font->base.ps_name); fail3: free (font->parent_to_subset); + if (font->base.font_name) + free (font->base.font_name); fail2: free (font->glyphs); fail1: _cairo_array_fini (&font->output); free (font); - fail0: - if (name) - free (name); return status; } @@ -310,7 +276,9 @@ cairo_truetype_font_destroy (cairo_truetype_font_t *font) { _cairo_array_fini (&font->string_offsets); free (font->base.widths); - free (font->base.base_font); + free (font->base.ps_name); + if (font->base.font_name) + free (font->base.font_name); free (font->parent_to_subset); free (font->glyphs); _cairo_array_fini (&font->output); @@ -328,7 +296,7 @@ cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font, return font->status; status = _cairo_array_allocate (&font->output, length, (void **) buffer); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; @@ -345,7 +313,7 @@ cairo_truetype_font_write (cairo_truetype_font_t *font, return; status = _cairo_array_append_multiple (&font->output, data, length); - if (status) + if (unlikely (status)) status = _cairo_truetype_font_set_error (font, status); } @@ -391,7 +359,7 @@ cairo_truetype_font_align_output (cairo_truetype_font_t *font, status = cairo_truetype_font_allocate_write_buffer (font, pad, &padding); - if (status) + if (unlikely (status)) return status; memset (padding, 0, pad); @@ -413,7 +381,7 @@ cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, { status = _cairo_array_append (&font->string_offsets, &font->last_boundary); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); font->last_offset = font->last_boundary; @@ -486,16 +454,16 @@ cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font, size = 0; status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font, tag, 0, NULL, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, buffer, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; @@ -527,26 +495,28 @@ cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, composite_glyph = &glyph_data->glyph; do { - if ((unsigned char *)(&composite_glyph->args[1]) >= end) + if ((unsigned char *)(&composite_glyph->args[1]) > end) return CAIRO_INT_STATUS_UNSUPPORTED; flags = be16_to_cpu (composite_glyph->flags); has_more_components = flags & TT_MORE_COMPONENTS; status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index); - if (status) + if (unlikely (status)) return status; composite_glyph->index = cpu_to_be16 (index); num_args = 1; if (flags & TT_ARG_1_AND_2_ARE_WORDS) num_args += 1; - if (flags & TT_WE_HAVE_A_SCALE) + + if (flags & TT_WE_HAVE_A_SCALE) num_args += 1; else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) num_args += 2; else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) - num_args += 3; - composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); + num_args += 4; + + composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); } while (has_more_components); return CAIRO_STATUS_SUCCESS; @@ -575,21 +545,21 @@ cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char*) &header, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); - + if (be16_to_cpu (header.index_to_loc_format) == 0) size = sizeof (int16_t) * (font->num_glyphs_in_face + 1); else size = sizeof (int32_t) * (font->num_glyphs_in_face + 1); u.bytes = malloc (size); - if (u.bytes == NULL) + if (unlikely (u.bytes == NULL)) return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_loca, 0, u.bytes, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); start_offset = _cairo_array_num_elements (&font->output); @@ -612,33 +582,33 @@ cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, size = end - begin; status = cairo_truetype_font_align_output (font, &next); - if (status) + if (unlikely (status)) goto FAIL; status = cairo_truetype_font_check_boundary (font, next); - if (status) + if (unlikely (status)) goto FAIL; font->glyphs[i].location = next - start_offset; status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); - if (status) + if (unlikely (status)) goto FAIL; if (size != 0) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_glyf, begin, buffer, &size); - if (status) + if (unlikely (status)) goto FAIL; status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); - if (status) + if (unlikely (status)) goto FAIL; } } status = cairo_truetype_font_align_output (font, &next); - if (status) + if (unlikely (status)) goto FAIL; font->glyphs[i].location = next - start_offset; @@ -664,20 +634,20 @@ cairo_truetype_font_write_head_table (cairo_truetype_font_t *font, size = 0; status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, NULL, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); font->checksum_index = _cairo_array_num_elements (&font->output) + 8; status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, buffer, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); - /* set checkSumAdjustment to 0 for table checksum calcualtion */ + /* set checkSumAdjustment to 0 for table checksum calculation */ *(uint32_t *)(buffer + 8) = 0; return CAIRO_STATUS_SUCCESS; @@ -695,12 +665,12 @@ cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long size = sizeof (tt_hhea_t); status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, (unsigned char *) hhea, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs)); @@ -728,7 +698,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char*) &hhea, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); num_hmetrics = be16_to_cpu(hhea.num_hmetrics); @@ -739,7 +709,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, status = cairo_truetype_font_allocate_write_buffer (font, long_entry_size, (unsigned char **) &p); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (font->glyphs[i].parent_index < num_hmetrics) { @@ -747,7 +717,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, TT_TAG_hmtx, font->glyphs[i].parent_index * long_entry_size, (unsigned char *) p, &long_entry_size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } else @@ -756,7 +726,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, (unsigned char *) p, &short_entry_size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, @@ -764,7 +734,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, num_hmetrics * long_entry_size + (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size, (unsigned char *) (p + 1), &short_entry_size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } font->base.widths[i] = be16_to_cpu (p[0]); @@ -789,7 +759,7 @@ cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char*) &header, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (be16_to_cpu (header.index_to_loc_format) == 0) @@ -817,12 +787,12 @@ cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, size = sizeof (tt_maxp_t); status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, (unsigned char *) maxp, &size); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs); @@ -862,7 +832,7 @@ cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font) table_buffer_length = font->num_tables * 16; status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length, &table_buffer); - if (status) + if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; @@ -920,28 +890,28 @@ cairo_truetype_font_generate (cairo_truetype_font_t *font, return font->status; status = cairo_truetype_font_write_offset_table (font); - if (status) + if (unlikely (status)) goto FAIL; status = cairo_truetype_font_align_output (font, &start); - if (status) + if (unlikely (status)) goto FAIL; end = 0; for (i = 0; i < font->num_tables; i++) { status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag); - if (status) + if (unlikely (status)) goto FAIL; end = _cairo_array_num_elements (&font->output); status = cairo_truetype_font_align_output (font, &next); - if (status) + if (unlikely (status)) goto FAIL; cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos, font->truetype_tables[i].tag, start, end); status = cairo_truetype_font_check_boundary (font, next); - if (status) + if (unlikely (status)) goto FAIL; start = next; @@ -1081,36 +1051,46 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, unsigned long num_strings = 0; status = _cairo_truetype_font_create (font_subset, &font); - if (status) + if (unlikely (status)) return status; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { unsigned short parent_glyph = font->scaled_font_subset->glyphs[i]; status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph); - if (status) + if (unlikely (status)) goto fail1; } cairo_truetype_font_create_truetype_table_list (font); status = cairo_truetype_font_generate (font, &data, &length, &string_offsets, &num_strings); - if (status) + if (unlikely (status)) goto fail1; - truetype_subset->base_font = strdup (font->base.base_font); - if (truetype_subset->base_font == NULL) { + truetype_subset->ps_name = strdup (font->base.ps_name); + if (unlikely (truetype_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } + if (font->base.font_name != NULL) { + truetype_subset->font_name = strdup (font->base.font_name); + if (unlikely (truetype_subset->font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + } else { + truetype_subset->font_name = NULL; + } + /* The widths array returned must contain only widths for the * glyphs in font_subset. Any subglyphs appended after * font_subset->num_glyphs are omitted. */ truetype_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); - if (truetype_subset->widths == NULL) { + if (unlikely (truetype_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; + goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em; @@ -1124,9 +1104,9 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, if (length) { truetype_subset->data = malloc (length); - if (truetype_subset->data == NULL) { + if (unlikely (truetype_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; + goto fail4; } memcpy (truetype_subset->data, data, length); @@ -1137,9 +1117,9 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, if (num_strings) { offsets_length = num_strings * sizeof (unsigned long); truetype_subset->string_offsets = malloc (offsets_length); - if (truetype_subset->string_offsets == NULL) { + if (unlikely (truetype_subset->string_offsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; + goto fail5; } memcpy (truetype_subset->string_offsets, string_offsets, offsets_length); @@ -1153,12 +1133,15 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, return CAIRO_STATUS_SUCCESS; - fail4: + fail5: free (truetype_subset->data); - fail3: + fail4: free (truetype_subset->widths); + fail3: + if (truetype_subset->font_name) + free (truetype_subset->font_name); fail2: - free (truetype_subset->base_font); + free (truetype_subset->ps_name); fail1: cairo_truetype_font_destroy (font); @@ -1168,7 +1151,9 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, void _cairo_truetype_subset_fini (cairo_truetype_subset_t *subset) { - free (subset->base_font); + free (subset->ps_name); + if (subset->font_name) + free (subset->font_name); free (subset->widths); free (subset->data); free (subset->string_offsets); @@ -1199,7 +1184,7 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, TT_TAG_cmap, table_offset, (unsigned char *) &buf, &size); - if (status) + if (unlikely (status)) return status; /* All table formats have the same first two words */ @@ -1209,14 +1194,14 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, size = be16_to_cpu (map->length); map = malloc (size); - if (map == NULL) + if (unlikely (map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, table_offset, (unsigned char *) map, &size); - if (status) + if (unlikely (status)) goto fail; num_segments = be16_to_cpu (map->segCountX2)/2; @@ -1299,21 +1284,21 @@ _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, TT_TAG_cmap, 0, (unsigned char *) &buf, &size); - if (status) + if (unlikely (status)) return status; cmap = (tt_cmap_t *) buf; num_tables = be16_to_cpu (cmap->num_tables); size = 4 + num_tables*sizeof(tt_cmap_index_t); cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4); - if (cmap == NULL) + if (unlikely (cmap == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, 0, (unsigned char *) cmap, &size); - if (status) + if (unlikely (status)) goto cleanup; /* Find a table with Unicode mapping */ @@ -1335,4 +1320,112 @@ cleanup: return status; } +cairo_int_status_t +_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, + char **ps_name_out, + char **font_name_out) +{ + cairo_status_t status; + const cairo_scaled_font_backend_t *backend; + tt_name_t *name; + tt_name_record_t *record; + unsigned long size; + int i, j; + char *ps_name = NULL; + char *font_name = NULL; + + backend = scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, + TT_TAG_name, 0, + NULL, + &size); + if (status) + return status; + + name = malloc (size); + if (name == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_name, 0, + (unsigned char *) name, + &size); + if (status) + goto fail; + + /* Extract the font name and PS name from the name table. At + * present this just looks for the Mac platform/Roman encoded font + * name. It should be extended to use any suitable font name in + * the name table. + */ + for (i = 0; i < be16_to_cpu(name->num_records); i++) { + record = &(name->records[i]); + if ((be16_to_cpu (record->platform) == 1) && + (be16_to_cpu (record->encoding) == 0)) { + + if (be16_to_cpu (record->name) == 4) { + font_name = malloc (be16_to_cpu(record->length) + 1); + if (font_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + strncpy(font_name, + ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), + be16_to_cpu (record->length)); + font_name[be16_to_cpu (record->length)] = 0; + } + + if (be16_to_cpu (record->name) == 6) { + ps_name = malloc (be16_to_cpu(record->length) + 1); + if (ps_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + strncpy(ps_name, + ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), + be16_to_cpu (record->length)); + ps_name[be16_to_cpu (record->length)] = 0; + } + + if (font_name && ps_name) + break; + } + } + + free (name); + + /* Ensure PS name does not contain any spaces */ + if (ps_name) { + for (i = 0, j = 0; ps_name[j]; j++) { + if (ps_name[j] == ' ') + continue; + ps_name[i++] = ps_name[j]; + } + ps_name[i] = '\0'; + } + + *ps_name_out = ps_name; + *font_name_out = font_name; + + return CAIRO_STATUS_SUCCESS; + +fail: + free (name); + + if (ps_name != NULL) + free (ps_name); + + if (font_name != NULL) + free (font_name); + + *ps_name_out = NULL; + *font_name_out = NULL; + + return status; +} + #endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/src/cairo-type1-fallback.c b/src/cairo-type1-fallback.c index 83ddc44..1023bbd 100644 --- a/src/cairo-type1-fallback.c +++ b/src/cairo-type1-fallback.c @@ -85,12 +85,11 @@ cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_status_t status; font = calloc (1, sizeof (cairo_type1_font_t)); - if (font == NULL) + if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font->widths = calloc (scaled_font_subset->num_glyphs, - sizeof (int)); - if (font->widths == NULL) { + font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int)); + if (unlikely (font->widths == NULL)) { free (font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -112,7 +111,7 @@ cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, &ctm, &font_options); status = font->type1_scaled_font->status; - if (status) + if (unlikely (status)) goto fail; _cairo_array_init (&font->contents, sizeof (unsigned char)); @@ -219,15 +218,15 @@ typedef struct _ps_path_info { } t1_path_info_t; static cairo_status_t -_charstring_move_to (void *closure, - cairo_point_t *point) +_charstring_move_to (void *closure, + const cairo_point_t *point) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx, dy; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 12); - if (status) + if (unlikely (status)) return status; dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; @@ -243,15 +242,15 @@ _charstring_move_to (void *closure, } static cairo_status_t -_charstring_line_to (void *closure, - cairo_point_t *point) +_charstring_line_to (void *closure, + const cairo_point_t *point) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx, dy; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 12); - if (status) + if (unlikely (status)) return status; dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; @@ -267,17 +266,17 @@ _charstring_line_to (void *closure, } static cairo_status_t -_charstring_curve_to (void *closure, - cairo_point_t *point1, - cairo_point_t *point2, - cairo_point_t *point3) +_charstring_curve_to (void *closure, + const cairo_point_t *point1, + const cairo_point_t *point2, + const cairo_point_t *point3) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx1, dy1, dx2, dy2, dx3, dy3; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 32); - if (status) + if (unlikely (status)) return status; dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x; @@ -309,7 +308,7 @@ _charstring_close_path (void *closure) return CAIRO_STATUS_SUCCESS; status = _cairo_array_grow_by (path_info->data, 2); - if (status) + if (unlikely (status)) return status; charstring_encode_command (path_info->data, CHARSTRING_closepath); @@ -363,7 +362,7 @@ cairo_type1_font_create_charstring (cairo_type1_font_t *font, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); } - if (status) + if (unlikely (status)) return status; metrics = &scaled_glyph->metrics; @@ -385,7 +384,7 @@ cairo_type1_font_create_charstring (cairo_type1_font_t *font, font->widths[subset_index] = metrics->x_advance; status = _cairo_array_grow_by (data, 30); - if (status) + if (unlikely (status)) return status; if (type == CAIRO_CHARSTRING_TYPE1) { @@ -413,12 +412,12 @@ cairo_type1_font_create_charstring (cairo_type1_font_t *font, _charstring_curve_to, _charstring_close_path, &path_info); - if (status) + if (unlikely (status)) return status; } status = _cairo_array_grow_by (data, 1); - if (status) + if (unlikely (status)) return status; charstring_encode_command (path_info.data, CHARSTRING_endchar); @@ -437,7 +436,7 @@ cairo_type1_font_write_charstrings (cairo_type1_font_t *font, _cairo_array_init (&data, sizeof (unsigned char)); status = _cairo_array_grow_by (&data, 1024); - if (status) + if (unlikely (status)) goto fail; _cairo_output_stream_printf (encrypted_output, @@ -449,15 +448,15 @@ cairo_type1_font_write_charstrings (cairo_type1_font_t *font, _cairo_array_truncate (&data, 0); /* four "random" bytes required by encryption algorithm */ status = _cairo_array_append_multiple (&data, zeros, 4); - if (status) - goto fail; + if (unlikely (status)) + break; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE1, &data); - if (status) - goto fail; + if (unlikely (status)) + break; charstring_encrypt (&data); length = _cairo_array_num_elements (&data); @@ -475,9 +474,9 @@ cairo_type1_font_write_charstrings (cairo_type1_font_t *font, length); _cairo_output_stream_printf (encrypted_output, " ND\n"); } + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); fail: - _cairo_scaled_font_thaw_cache (font->type1_scaled_font); _cairo_array_fini (&data); return status; } @@ -590,7 +589,7 @@ cairo_type1_font_write_private_dict (cairo_type1_font_t *font, _cairo_output_stream_printf (encrypted_output, " dup /Private 9 dict dup begin\n" "/RD {string currentfile exch readstring pop}" - " executeonly def\n" + " bind executeonly def\n" "/ND {noaccess def} executeonly def\n" "/NP {noaccess put} executeonly def\n" "/BlueValues [] def\n" @@ -599,7 +598,7 @@ cairo_type1_font_write_private_dict (cairo_type1_font_t *font, "/password 5839 def\n"); status = cairo_type1_font_write_charstrings (font, encrypted_output); - if (status) + if (unlikely (status)) goto fail; _cairo_output_stream_printf (encrypted_output, @@ -651,7 +650,7 @@ cairo_type1_font_write (cairo_type1_font_t *font, font->header_size = _cairo_output_stream_get_position (font->output); status = cairo_type1_font_write_private_dict (font, name); - if (status) + if (unlikely (status)) return status; font->data_size = _cairo_output_stream_get_position (font->output) - @@ -671,7 +670,7 @@ cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) cairo_int_status_t status; status = _cairo_array_grow_by (&font->contents, 4096); - if (status) + if (unlikely (status)) return status; font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font); @@ -679,7 +678,7 @@ cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) return _cairo_output_stream_destroy (font->output); status = cairo_type1_font_write (font, name); - if (status) + if (unlikely (status)) return status; font->data = _cairo_array_index (&font->contents, 0); @@ -714,21 +713,21 @@ _cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, unsigned int i, len; status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode); - if (status) + if (unlikely (status)) return status; status = cairo_type1_font_generate (font, name); - if (status) + if (unlikely (status)) goto fail1; type1_subset->base_font = strdup (name); - if (type1_subset->base_font == NULL) { + if (unlikely (type1_subset->base_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } type1_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); - if (type1_subset->widths == NULL) { + if (unlikely (type1_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } @@ -745,7 +744,7 @@ _cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, length = font->header_size + font->data_size + font->trailer_size; type1_subset->data = malloc (length); - if (type1_subset->data == NULL) { + if (unlikely (type1_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } @@ -816,13 +815,13 @@ _cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, cairo_array_t charstring; status = cairo_type1_font_create (scaled_font_subset, &font, FALSE); - if (status) + if (unlikely (status)) return status; _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t)); type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); - if (type2_subset->widths == NULL) { + if (unlikely (type2_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } @@ -831,18 +830,18 @@ _cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { _cairo_array_init (&charstring, sizeof (unsigned char)); status = _cairo_array_grow_by (&charstring, 32); - if (status) + if (unlikely (status)) goto fail2; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE2, &charstring); - if (status) + if (unlikely (status)) goto fail2; status = _cairo_array_append (&type2_subset->charstrings, &charstring); - if (status) + if (unlikely (status)) goto fail2; } _cairo_scaled_font_thaw_cache (font->type1_scaled_font); diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c index bbff33d..fe74dc6 100644 --- a/src/cairo-type1-subset.c +++ b/src/cairo-type1-subset.c @@ -126,7 +126,7 @@ _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font; face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); - if (face == NULL) + if (unlikely (face == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (FT_Get_PS_Font_Info(face, &font_info) != 0) { @@ -154,7 +154,7 @@ _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, if (face->family_name) { font->base.base_font = strdup (face->family_name); - if (font->base.base_font == NULL) { + if (unlikely (font->base.base_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } @@ -167,7 +167,7 @@ _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, } font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]); - if (font->glyphs == NULL) { + if (unlikely (font->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } @@ -467,7 +467,7 @@ cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) end = (unsigned char *) in + font->eexec_segment_size; font->cleartext = malloc (font->eexec_segment_size); - if (font->cleartext == NULL) + if (unlikely (font->cleartext == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); out = font->cleartext; @@ -553,17 +553,27 @@ cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *f error = FT_Load_Glyph (font->face, i, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); - if (error != 0) + if (error != FT_Err_Ok) { + /* propagate fatal errors from FreeType */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return CAIRO_INT_STATUS_UNSUPPORTED; + } font->glyphs[i].width = font->face->glyph->metrics.horiAdvance; error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer); - if (error != 0) + if (error != FT_Err_Ok) { + /* propagate fatal errors from FreeType */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return CAIRO_INT_STATUS_UNSUPPORTED; + } font->glyphs[i].name = strdup (buffer); - if (font->glyphs[i].name == NULL) + if (unlikely (font->glyphs[i].name == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -828,7 +838,7 @@ cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font, int command; charstring = malloc (encrypted_charstring_length); - if (charstring == NULL) + if (unlikely (charstring == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_type1_font_subset_decrypt_charstring ((const unsigned char *) @@ -856,11 +866,11 @@ cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font, * make sure those glyphs are present in the subset * under their standard names. */ status = use_standard_encoding_glyph (font, stack[3]); - if (status) + if (unlikely (status)) return status; status = use_standard_encoding_glyph (font, stack[4]); - if (status) + if (unlikely (status)) return status; sp = 0; @@ -896,18 +906,18 @@ write_used_glyphs (cairo_type1_font_subset_t *font, "/%.*s %d %s ", name_length, name, charstring_length, font->rd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); - if (status) + if (unlikely (status)) return status; status = cairo_type1_font_subset_write_encrypted (font, charstring, charstring_length); - if (status) + if (unlikely (status)) return status; length = snprintf (buffer, sizeof buffer, "%s\n", font->nd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -973,7 +983,7 @@ cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, cairo_status_t status = func (font, name, name_length, charstring, charstring_length); - if (status) + if (unlikely (status)) return status; } } @@ -1030,7 +1040,7 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, dict_start = p; status = cairo_type1_font_subset_get_glyph_names_and_widths (font); - if (status) + if (unlikely (status)) return status; /* Now that we have the private dictionary broken down in @@ -1042,7 +1052,7 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, font->cleartext_end, cairo_type1_font_subset_look_for_seac, &p); - if (status) + if (unlikely (status)) return status; closefile_token = find_token (p, font->cleartext_end, "closefile"); @@ -1050,13 +1060,13 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, return CAIRO_INT_STATUS_UNSUPPORTED; status = cairo_type1_font_subset_get_glyph_names_and_widths (font); - if (status) + if (unlikely (status)) return status; /* We're ready to start outputting. First write the header, * i.e. the public part of the font dict.*/ status = cairo_type1_font_subset_write_header (font, name); - if (status) + if (unlikely (status)) return status; font->base.header_size = _cairo_output_stream_get_position (font->output); @@ -1066,21 +1076,21 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, * to the /CharStrings token. */ status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, charstrings - font->cleartext); - if (status) + if (unlikely (status)) return status; /* Write out new charstring count */ length = snprintf (buffer, sizeof buffer, "/CharStrings %d", font->num_glyphs); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); - if (status) + if (unlikely (status)) return status; /* Write out text between the charstring count and the first * charstring definition */ status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end, dict_start - glyph_count_end); - if (status) + if (unlikely (status)) return status; /* Write out the charstring definitions for each of the glyphs in @@ -1090,14 +1100,14 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, font->cleartext_end, write_used_glyphs, &p); - if (status) + if (unlikely (status)) return status; /* Output what's left between the end of the glyph definitions and * the end of the private dict to the output. */ status = cairo_type1_font_subset_write_encrypted (font, p, closefile_token - p + strlen ("closefile") + 1); - if (status) + if (unlikely (status)) return status; _cairo_output_stream_write (font->output, "\n", 1); @@ -1148,11 +1158,11 @@ cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, cairo_status_t status; status = cairo_type1_font_subset_find_segments (font); - if (status) + if (unlikely (status)) return status; status = cairo_type1_font_subset_decrypt_eexec_segment (font); - if (status) + if (unlikely (status)) return status; /* Determine which glyph definition delimiters to use. */ @@ -1171,14 +1181,14 @@ cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, font->hex_column = 0; status = cairo_type1_font_subset_write_private_dict (font, name); - if (status) + if (unlikely (status)) return status; font->base.data_size = _cairo_output_stream_get_position (font->output) - font->base.header_size; status = cairo_type1_font_subset_write_trailer (font); - if (status) + if (unlikely (status)) return status; font->base.trailer_size = @@ -1200,12 +1210,12 @@ cairo_type1_font_subset_generate (void *abstract_font, ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font; font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); - if (font->face == NULL) + if (unlikely (font->face == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->type1_length = font->face->stream->size; font->type1_data = malloc (font->type1_length); - if (font->type1_data == NULL) { + if (unlikely (font->type1_data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } @@ -1229,7 +1239,7 @@ cairo_type1_font_subset_generate (void *abstract_font, } status = _cairo_array_grow_by (&font->contents, 4096); - if (status) + if (unlikely (status)) goto fail; font->output = _cairo_output_stream_create (type1_font_write, NULL, font); @@ -1239,7 +1249,7 @@ cairo_type1_font_subset_generate (void *abstract_font, } status = cairo_type1_font_subset_write (font, name); - if (status) + if (unlikely (status)) goto fail; font->base.data = _cairo_array_index (&font->contents, 0); @@ -1296,7 +1306,7 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font); status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode); - if (status) + if (unlikely (status)) return status; for (i = 0; i < scaled_font_subset->num_glyphs; i++) { @@ -1305,7 +1315,7 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, } status = cairo_type1_font_subset_generate (&font, name); - if (status) + if (unlikely (status)) goto fail1; if (font.base.base_font) { @@ -1315,11 +1325,11 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, scaled_font_subset->font_id, scaled_font_subset->subset_id); type1_subset->base_font = strdup (buf); } - if (type1_subset->base_font == NULL) + if (unlikely (type1_subset->base_font == NULL)) goto fail1; type1_subset->widths = calloc (sizeof (int), font.num_glyphs); - if (type1_subset->widths == NULL) + if (unlikely (type1_subset->widths == NULL)) goto fail2; for (i = 0; i < font.base.num_glyphs; i++) { if (font.glyphs[i].subset_index < 0) @@ -1339,7 +1349,7 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, font.base.data_size + font.base.trailer_size; type1_subset->data = malloc (length); - if (type1_subset->data == NULL) + if (unlikely (type1_subset->data == NULL)) goto fail3; memcpy (type1_subset->data, diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c index c84f5f0..104da53 100644 --- a/src/cairo-type3-glyph-surface.c +++ b/src/cairo-type3-glyph-surface.c @@ -46,16 +46,19 @@ static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; cairo_surface_t * -_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, - cairo_output_stream_t *stream, +_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, + cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, - cairo_scaled_font_subsets_t *font_subsets) + cairo_scaled_font_subsets_t *font_subsets) { cairo_type3_glyph_surface_t *surface; cairo_matrix_t invert_y_axis; + if (unlikely (stream != NULL && stream->status)) + return _cairo_surface_create_in_error (stream->status); + surface = malloc (sizeof (cairo_type3_glyph_surface_t)); - if (surface == NULL) + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_type3_glyph_surface_backend, @@ -87,18 +90,12 @@ _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, cairo_matrix_t *image_matrix) { cairo_status_t status; - cairo_image_surface_t *image_mask; - - /* The only image type supported by Type 3 fonts are 1-bit image - * masks */ - if (image->format == CAIRO_FORMAT_A1) { - image_mask = image; - } else { - image_mask = _cairo_image_surface_clone (image, CAIRO_FORMAT_A1); - status = cairo_surface_status (&image->base); - if (status) - return status; - } + + /* The only image type supported by Type 3 fonts are 1-bit masks */ + image = _cairo_image_surface_coerce (image, CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; _cairo_output_stream_printf (surface->stream, "q %f %f %f %f %f %f cm\n", @@ -109,21 +106,19 @@ _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, image_matrix->x0, image_matrix->y0); - status = surface->emit_image (image_mask, surface->stream); + status = surface->emit_image (image, surface->stream); + cairo_surface_destroy (&image->base); _cairo_output_stream_printf (surface->stream, "Q\n"); - if (image_mask != image) - cairo_surface_destroy (&image_mask->base); - return status; } static cairo_status_t _cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface, cairo_image_surface_t *image, - cairo_matrix_t *pattern_matrix) + const cairo_matrix_t *pattern_matrix) { cairo_matrix_t mat, upside_down; cairo_status_t status; @@ -182,10 +177,11 @@ _cairo_type3_glyph_surface_intersect_clip_path (void *abstract_surface, static cairo_int_status_t _cairo_type3_glyph_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_surface_pattern_t *pattern; + const cairo_surface_pattern_t *pattern; cairo_image_surface_t *image; void *image_extra; cairo_status_t status; @@ -193,9 +189,9 @@ _cairo_type3_glyph_surface_paint (void *abstract_surface, if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_IMAGE_FALLBACK; - pattern = (cairo_surface_pattern_t *) source; + pattern = (const cairo_surface_pattern_t *) source; status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (status) + if (unlikely (status)) goto fail; status = _cairo_type3_glyph_surface_emit_image_pattern (surface, @@ -211,22 +207,24 @@ fail: static cairo_int_status_t _cairo_type3_glyph_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { - return _cairo_type3_glyph_surface_paint (abstract_surface, op, mask); + return _cairo_type3_glyph_surface_paint (abstract_surface, op, mask, extents); } static cairo_int_status_t _cairo_type3_glyph_surface_stroke (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_type3_glyph_surface_t *surface = abstract_surface; @@ -240,11 +238,12 @@ _cairo_type3_glyph_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_type3_glyph_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -259,11 +258,12 @@ _cairo_type3_glyph_surface_fill (void *abstract_surface, static cairo_int_status_t _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -277,7 +277,7 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, /* We require the matrix to be invertable. */ ctm_inverse = scaled_font->ctm; status = cairo_matrix_invert (&ctm_inverse); - if (status) + if (unlikely (status)) return CAIRO_INT_STATUS_IMAGE_FALLBACK; cairo_matrix_multiply (&new_ctm, &scaled_font->ctm, &ctm_inverse); @@ -285,6 +285,8 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, &scaled_font->font_matrix, &new_ctm, &scaled_font->options); + if (unlikely (font->status)) + return font->status; status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, NULL, 0, @@ -310,6 +312,8 @@ static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* cairo_type3_glyph_surface_copy_page */ NULL, /* _cairo_type3_glyph_surface_show_page */ NULL, /* set_clip_region */ @@ -352,7 +356,7 @@ _cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *sur CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); - if (status) + if (unlikely (status)) return status; image = scaled_glyph->surface; @@ -380,6 +384,9 @@ _cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract { cairo_type3_glyph_surface_t *surface = abstract_surface; + if (unlikely (surface->base.status)) + return; + _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, use_font_subset, closure); @@ -394,7 +401,13 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, cairo_status_t status, status2; cairo_output_stream_t *null_stream; + if (unlikely (surface->base.status)) + return surface->base.status; + null_stream = _cairo_null_stream_create (); + if (unlikely (null_stream->status)) + return null_stream->status; + _cairo_type3_glyph_surface_set_stream (surface, null_stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); @@ -414,7 +427,7 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, status = _cairo_meta_surface_replay (scaled_glyph->meta_surface, &surface->base); - if (status) + if (unlikely (status)) goto cleanup; status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); @@ -445,9 +458,11 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status, status2; double x_advance, y_advance; - cairo_output_stream_t *mem_stream; cairo_matrix_t font_matrix_inverse; + if (unlikely (surface->base.status)) + return surface->base.status; + _cairo_type3_glyph_surface_set_stream (surface, stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); @@ -495,7 +510,13 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, - _cairo_fixed_to_double (bbox->p1.y)); if (status == CAIRO_STATUS_SUCCESS) { + cairo_output_stream_t *mem_stream; + mem_stream = _cairo_memory_stream_create (); + status = mem_stream->status; + if (unlikely (status)) + goto FAIL; + _cairo_type3_glyph_surface_set_stream (surface, mem_stream); _cairo_output_stream_printf (surface->stream, "q\n"); @@ -520,6 +541,7 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); + FAIL: _cairo_scaled_font_thaw_cache (surface->scaled_font); return status; diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 12e1a20..1edd24f 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -41,8 +41,10 @@ #include "cairo.h" #include "cairo-fixed-type-private.h" +#include "cairo-reference-count-private.h" typedef struct _cairo_array cairo_array_t; +typedef struct _cairo_backend cairo_backend_t; typedef struct _cairo_cache cairo_cache_t; typedef struct _cairo_clip cairo_clip_t; typedef struct _cairo_clip_path cairo_clip_path_t; @@ -52,11 +54,11 @@ typedef struct _cairo_gstate cairo_gstate_t; typedef struct _cairo_hash_entry cairo_hash_entry_t; typedef struct _cairo_hash_table cairo_hash_table_t; typedef struct _cairo_image_surface cairo_image_surface_t; +typedef struct _cairo_mime_data cairo_mime_data_t; typedef struct _cairo_output_stream cairo_output_stream_t; typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; typedef struct _cairo_path_fixed cairo_path_fixed_t; typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; -typedef struct _cairo_region cairo_region_t; typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; typedef struct _cairo_solid_pattern cairo_solid_pattern_t; @@ -120,17 +122,6 @@ struct _cairo_font_options { cairo_hint_metrics_t hint_metrics; }; -struct _cairo_cache { - cairo_hash_table_t *hash_table; - - cairo_destroy_func_t entry_destroy; - - unsigned long max_size; - unsigned long size; - - int freeze_count; -}; - /* XXX: Right now, the _cairo_color structure puts unpremultiplied color in the doubles and premultiplied color in the shorts. Yes, this is crazy insane, (but at least we don't export this @@ -212,42 +203,35 @@ typedef struct _cairo_trapezoid { cairo_line_t left, right; } cairo_trapezoid_t; -struct _cairo_rectangle_int16 { - int16_t x, y; - uint16_t width, height; -}; +typedef struct _cairo_point_int { + int x, y; +} cairo_point_int_t; -struct _cairo_rectangle_int32 { - int32_t x, y; - uint32_t width, height; -}; - -struct _cairo_point_int16 { - int16_t x, y; -}; - -struct _cairo_point_int32 { - int32_t x, y; -}; +#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS) +#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS) -#if CAIRO_FIXED_BITS == 32 && CAIRO_FIXED_FRAC_BITS >= 16 -typedef struct _cairo_rectangle_int16 cairo_rectangle_int_t; -typedef struct _cairo_point_int16 cairo_point_int_t; -#define CAIRO_RECT_INT_MIN (INT16_MIN >> (CAIRO_FIXED_FRAC_BITS - 16)) -#define CAIRO_RECT_INT_MAX (INT16_MAX >> (CAIRO_FIXED_FRAC_BITS - 16)) -#elif CAIRO_FIXED_BITS == 32 -typedef struct _cairo_rectangle_int32 cairo_rectangle_int_t; -typedef struct _cairo_point_int32 cairo_point_int_t; -#define CAIRO_RECT_INT_MIN (INT32_MIN >> CAIRO_FIXED_FRAC_BITS) -#define CAIRO_RECT_INT_MAX (INT32_MAX >> CAIRO_FIXED_FRAC_BITS) -#else -#error Not sure how to pick a cairo_rectangle_int_t and cairo_point_int_t for your CAIRO_FIXED_BITS! -#endif - -typedef struct _cairo_box_int { - cairo_point_int_t p1; - cairo_point_int_t p2; -} cairo_box_int_t; +/* Rectangles that take part in a composite operation. + * + * This defines four translations that define which pixels of the + * source pattern, mask, clip and destination surface take part in a + * general composite operation. The idea is that the pixels at + * + * (i,j)+(src.x, src.y) of the source, + * (i,j)+(mask.x, mask.y) of the mask, + * (i,j)+(clip.x, clip.y) of the clip and + * (i,j)+(dst.x, dst.y) of the destination + * + * all combine together to form the result at (i,j)+(dst.x,dst.y), + * for i,j ranging in [0,width) and [0,height) respectively. + */ +typedef struct _cairo_composite_rectangles { + cairo_point_int_t src; + cairo_point_int_t mask; + cairo_point_int_t clip; + cairo_point_int_t dst; + int width; + int height; +} cairo_composite_rectangles_t; typedef enum _cairo_direction { CAIRO_DIRECTION_FORWARD, @@ -262,9 +246,7 @@ typedef enum _cairo_clip_mode { typedef struct _cairo_edge { cairo_line_t edge; - int clockWise; - - cairo_fixed_t current_x; + int dir; } cairo_edge_t; typedef struct _cairo_polygon { @@ -280,19 +262,25 @@ typedef struct _cairo_polygon { cairo_edge_t edges_embedded[32]; } cairo_polygon_t; +typedef cairo_warn cairo_status_t +(*cairo_spline_add_point_func_t) (void *closure, + const cairo_point_t *point); + typedef struct _cairo_spline_knots { cairo_point_t a, b, c, d; } cairo_spline_knots_t; + typedef struct _cairo_spline { + cairo_spline_add_point_func_t add_point_func; + void *closure; + cairo_spline_knots_t knots; cairo_slope_t initial_slope; cairo_slope_t final_slope; - int num_points; - int points_size; - cairo_point_t *points; - cairo_point_t points_embedded[64]; + cairo_bool_t has_point; + cairo_point_t last_point; } cairo_spline_t; typedef struct _cairo_pen_vertex { @@ -342,4 +330,12 @@ typedef enum _cairo_image_transparency { CAIRO_IMAGE_UNKNOWN } cairo_image_transparency_t; +struct _cairo_mime_data { + cairo_reference_count_t ref_count; + unsigned char *data; + unsigned int length; + cairo_destroy_func_t destroy; + void *closure; +}; + #endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index ddcbc77..5ab9751 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -92,6 +92,7 @@ _cairo_user_scaled_font_create_meta_context (cairo_user_scaled_font_t *scaled_fo cairo_set_matrix (cr, &scaled_font->base.scale); cairo_set_font_size (cr, 1.0); cairo_set_font_options (cr, &scaled_font->base.options); + cairo_set_source_rgb (cr, 1., 1., 1.); return cr; } @@ -118,7 +119,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, _cairo_scaled_glyph_index(scaled_glyph), cr, &extents); else - status = CAIRO_STATUS_USER_FONT_ERROR; + status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; if (status == CAIRO_STATUS_SUCCESS) status = cairo_status (cr); @@ -127,7 +128,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, cairo_destroy (cr); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (meta_surface); return status; } @@ -152,7 +153,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); cairo_surface_destroy (null_surface); status = analysis_surface->status; - if (status) + if (unlikely (status)) return status; _cairo_analysis_surface_set_ctm (analysis_surface, @@ -162,7 +163,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); cairo_surface_destroy (analysis_surface); - if (status) + if (unlikely (status)) return status; _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); @@ -187,7 +188,6 @@ _cairo_user_scaled_glyph_init (void *abstract_font, if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { cairo_surface_t *surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_format_t format; int width, height; @@ -216,7 +216,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); status = _cairo_meta_surface_replay (meta_surface, surface); - if (status) { + if (unlikely (status)) { cairo_surface_destroy(surface); return status; } @@ -233,7 +233,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, status = _cairo_meta_surface_get_path (meta_surface, path); - if (status) { + if (unlikely (status)) { _cairo_path_fixed_destroy (path); return status; } @@ -261,12 +261,16 @@ _cairo_user_ucs4_to_index (void *abstract_font, status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base, ucs4, &glyph); + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + goto not_implemented; + if (status != CAIRO_STATUS_SUCCESS) { status = _cairo_scaled_font_set_error (&scaled_font->base, status); glyph = 0; } } else { +not_implemented: glyph = ucs4; } @@ -301,10 +305,11 @@ _cairo_user_text_to_glyphs (void *abstract_font, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); - if (status != CAIRO_STATUS_SUCCESS) + if (status != CAIRO_STATUS_SUCCESS && + status != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) return status; - if (*num_glyphs < 0) { + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED || *num_glyphs < 0) { if (orig_glyphs != *glyphs) { cairo_glyph_free (*glyphs); *glyphs = orig_glyphs; @@ -337,64 +342,14 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ cairo_scaled_font_t **scaled_font); static cairo_status_t -_cairo_user_scaled_font_get_implementation (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face_out) +_cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) { - static cairo_user_data_key_t twin_font_face_key; - - cairo_font_face_t *face; - cairo_status_t status; - - face = cairo_font_face_get_user_data (&toy_face->base, - &twin_font_face_key); - if (!face) { - face = _cairo_font_face_twin_create (cairo_toy_font_face_get_slant (&toy_face->base), - cairo_toy_font_face_get_weight (&toy_face->base)); - - status = cairo_font_face_set_user_data (&toy_face->base, - &twin_font_face_key, - face, - (cairo_destroy_func_t) cairo_font_face_destroy); - - if (status) { - cairo_font_face_destroy (face); - return status; - } - } - - *font_face_out = face; - return CAIRO_STATUS_SUCCESS; + return _cairo_font_face_twin_create_for_toy (toy_face, font_face); } -static cairo_status_t -_cairo_user_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *font_options, - cairo_scaled_font_t **font) -{ - cairo_font_face_t *face; - cairo_status_t status; - - status = _cairo_user_scaled_font_get_implementation (toy_face, &face); - if (status) - return status; - - status = _cairo_user_font_face_scaled_font_create (face, - font_matrix, - ctm, - font_options, - font); - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { +static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { CAIRO_FONT_TYPE_USER, - _cairo_user_scaled_font_get_implementation, - _cairo_user_scaled_font_create_toy, /* create_toy */ NULL, /* scaled_font_fini */ _cairo_user_scaled_glyph_init, _cairo_user_text_to_glyphs, @@ -421,7 +376,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ font_face->immutable = TRUE; user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t)); - if (user_scaled_font == NULL) + if (unlikely (user_scaled_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_scaled_font_init (&user_scaled_font->base, @@ -429,7 +384,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ font_matrix, ctm, options, &_cairo_user_scaled_font_backend); - if (status) { + if (unlikely (status)) { free (user_scaled_font); return status; } @@ -485,6 +440,9 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ cr, &font_extents); + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + status = CAIRO_STATUS_SUCCESS; + if (status == CAIRO_STATUS_SUCCESS) status = cairo_status (cr); @@ -516,10 +474,10 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ return status; } -static const cairo_font_face_backend_t _cairo_user_font_face_backend = { +const cairo_font_face_backend_t _cairo_user_font_face_backend = { CAIRO_FONT_TYPE_USER, + _cairo_user_font_face_create_for_toy, NULL, /* destroy */ - NULL, /* direct implementation */ _cairo_user_font_face_scaled_font_create }; diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c index a9a9353..e62e881 100644 --- a/src/cairo-win32-font.c +++ b/src/cairo-win32-font.c @@ -521,11 +521,8 @@ _cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) /* implement the font backend interface */ static cairo_status_t -_cairo_win32_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font_out) +_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) { LOGFONTW logfont; uint16_t *face_name; @@ -537,12 +534,11 @@ _cairo_win32_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, if (status) return status; - if (face_name_len > LF_FACESIZE - 1) { - free (face_name); - return _cairo_error (CAIRO_STATUS_INVALID_STRING); - } + if (face_name_len > LF_FACESIZE - 1) + face_name_len = LF_FACESIZE - 1; - memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * (face_name_len + 1)); + memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len); + logfont.lfFaceName[face_name_len] = 0; free (face_name); logfont.lfHeight = 0; /* filled in later */ @@ -583,12 +579,9 @@ _cairo_win32_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - if (!logfont.lfFaceName) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + *font_face = cairo_win32_font_face_create_for_logfontw (&logfont); - return _win32_scaled_font_create (&logfont, NULL, &toy_face->base, - font_matrix, ctm, options, - scaled_font_out); + return CAIRO_STATUS_SUCCESS; } static void @@ -1181,12 +1174,12 @@ _add_glyph (cairo_glyph_state_t *state, if (status) return status; state->start_x = logical_x; + } else { + dx = logical_x - state->last_x; + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; } - - dx = logical_x - state->last_x; - status = _cairo_array_append (&state->dx, &dx); - if (status) - return status; } else { state->start_x = logical_x; } @@ -1355,19 +1348,19 @@ _cairo_win32_scaled_font_glyph_init (void *abstract_font, } static cairo_int_status_t -_cairo_win32_scaled_font_show_glyphs (void *abstract_font, - cairo_operator_t op, - cairo_pattern_t *pattern, - cairo_surface_t *generic_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - int *remaining_glyphs) +_cairo_win32_scaled_font_show_glyphs (void *abstract_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + int *remaining_glyphs) { cairo_win32_scaled_font_t *scaled_font = abstract_font; cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; @@ -1833,8 +1826,6 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { CAIRO_FONT_TYPE_WIN32, - NULL, - _cairo_win32_scaled_font_create_toy, _cairo_win32_scaled_font_fini, _cairo_win32_scaled_font_glyph_init, NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */ @@ -1900,17 +1891,17 @@ _cairo_win32_font_face_scaled_font_create (void *abstract_face, font); } -static const cairo_font_face_backend_t _cairo_win32_font_face_backend = { +const cairo_font_face_backend_t _cairo_win32_font_face_backend = { CAIRO_FONT_TYPE_WIN32, + _cairo_win32_font_face_create_for_toy, _cairo_win32_font_face_destroy, - NULL, /* direct implementation */ _cairo_win32_font_face_scaled_font_create }; /** * cairo_win32_font_face_create_for_logfontw_hfont: * @logfont: A #LOGFONTW structure specifying the font to use. - * If hfont is null then the lfHeight, lfWidth, lfOrientation and lfEscapement + * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and * lfEscapement must be zero. * @font: An #HFONT that can be used when the font matrix is a scale by diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c index 2c2f061..e7ff3bb 100644 --- a/src/cairo-win32-printing-surface.c +++ b/src/cairo-win32-printing-surface.c @@ -1,7 +1,7 @@ /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * - * Copyright © 2007 Adrian Johnson + * Copyright © 2007, 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -52,6 +52,7 @@ #include "cairo-win32-private.h" #include "cairo-meta-surface-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-image-info-private.h" #include <windows.h> @@ -75,6 +76,14 @@ # define GRADIENT_FILL_RECT_H 0x00 #endif +#if !defined(CHECKJPEGFORMAT) +# define CHECKJPEGFORMAT 0x1017 +#endif + +#if !defined(CHECKPNGFORMAT) +# define CHECKPNGFORMAT 0x1018 +#endif + #define PELS_72DPI ((LONG)(72. / 0.0254)) static const cairo_surface_backend_t cairo_win32_printing_surface_backend; @@ -99,6 +108,20 @@ _cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface) surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; } +static void +_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface) +{ + DWORD word; + + word = CHECKJPEGFORMAT; + if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; + + word = CHECKPNGFORMAT; + if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; +} + static cairo_int_status_t analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) { @@ -294,7 +317,7 @@ _cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surfa static cairo_status_t _cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface, - cairo_pattern_t *source) + const cairo_pattern_t *source) { cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source; COLORREF color; @@ -337,7 +360,7 @@ _cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface, static cairo_status_t _cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { RECT clip; cairo_status_t status; @@ -350,7 +373,7 @@ _cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surfac FillRect (surface->dc, &clip, surface->brush); _cairo_win32_printing_surface_done_solid_brush (surface); - return 0; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -411,7 +434,9 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa surface->content = CAIRO_CONTENT_COLOR; _cairo_pattern_init_solid (&black, CAIRO_COLOR_BLACK, CAIRO_CONTENT_COLOR); source = (cairo_pattern_t*) &black; - _cairo_win32_printing_surface_paint_solid_pattern (surface, source); + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, source); + if (status) + return status; } for (y_tile = top; y_tile < bottom; y_tile++) { @@ -484,13 +509,89 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t *surfa return status; } +static cairo_int_status_t +_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned int *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned int mime_data_length; + cairo_int_status_t status; + DWORD result; + + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned int *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned int mime_data_length; + + cairo_int_status_t status; + DWORD result; + + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surface, cairo_surface_pattern_t *pattern) { cairo_status_t status; cairo_extend_t extend; - cairo_surface_attributes_t pat_attr; cairo_image_surface_t *image; void *image_extra; cairo_surface_t *opaque_surface; @@ -502,6 +603,11 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf int x_tile, y_tile, left, right, top, bottom; RECT clip; const cairo_color_t *background_color; + const unsigned char *mime_data; + unsigned int mime_size; + cairo_image_info_t mime_info; + cairo_bool_t use_mime; + DWORD mime_type; /* If we can't use StretchDIBits with this surface, we can't do anything * here. @@ -531,7 +637,26 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf goto CLEANUP_IMAGE; } - if (image->format != CAIRO_FORMAT_RGB24) { + mime_type = BI_JPEG; + status = _cairo_win32_printing_surface_check_jpeg (surface, + pattern->surface, + &mime_data, + &mime_size, + &mime_info); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + mime_type = BI_PNG; + status = _cairo_win32_printing_surface_check_png (surface, + pattern->surface, + &mime_data, + &mime_size, + &mime_info); + } + if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + use_mime = (status == CAIRO_STATUS_SUCCESS); + + if (!use_mime && image->format != CAIRO_FORMAT_RGB24) { cairo_surface_pattern_t opaque_pattern; opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, @@ -576,14 +701,14 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf } bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = opaque_image->width; - bi.bmiHeader.biHeight = -opaque_image->height; - bi.bmiHeader.biSizeImage = 0; + bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width; + bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height; + bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0; bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB; bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; @@ -597,7 +722,7 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf _cairo_matrix_to_win32_xform (&m, &xform); if (! SetWorldTransform (surface->dc, &xform)) { - status = _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); goto CLEANUP_OPAQUE_IMAGE; } @@ -625,9 +750,9 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf opaque_image->height, 0, 0, - opaque_image->width, - opaque_image->height, - opaque_image->data, + use_mime ? mime_info.width : opaque_image->width, + use_mime ? mime_info.height : opaque_image->height, + use_mime ? mime_data : opaque_image->data, &bi, DIB_RGB_COLORS, SRCCOPY)) @@ -820,7 +945,7 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa static cairo_int_status_t _cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, - cairo_pattern_t *pattern) + const cairo_pattern_t *pattern) { cairo_status_t status; @@ -857,7 +982,8 @@ typedef struct _win32_print_path_info { } win32_path_info_t; static cairo_status_t -_cairo_win32_printing_surface_path_move_to (void *closure, cairo_point_t *point) +_cairo_win32_printing_surface_path_move_to (void *closure, + const cairo_point_t *point) { win32_path_info_t *path_info = closure; @@ -879,7 +1005,8 @@ _cairo_win32_printing_surface_path_move_to (void *closure, cairo_point_t *point) } static cairo_status_t -_cairo_win32_printing_surface_path_line_to (void *closure, cairo_point_t *point) +_cairo_win32_printing_surface_path_line_to (void *closure, + const cairo_point_t *point) { win32_path_info_t *path_info = closure; @@ -902,9 +1029,9 @@ _cairo_win32_printing_surface_path_line_to (void *closure, cairo_point_t *point) static cairo_status_t _cairo_win32_printing_surface_path_curve_to (void *closure, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d) + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) { win32_path_info_t *path_info = closure; POINT points[3]; @@ -1034,9 +1161,10 @@ _cairo_win32_printing_surface_get_font_options (void *abstract_ } static cairo_int_status_t -_cairo_win32_printing_surface_paint (void *abstract_surface, - cairo_operator_t op, - cairo_pattern_t *source) +_cairo_win32_printing_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { cairo_win32_surface_t *surface = abstract_surface; cairo_solid_pattern_t clear; @@ -1105,15 +1233,16 @@ _cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) } static cairo_int_status_t -_cairo_win32_printing_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 *stroke_ctm, - cairo_matrix_t *stroke_ctm_inverse, - double tolerance, - cairo_antialias_t antialias) +_cairo_win32_printing_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *stroke_ctm, + cairo_matrix_t *stroke_ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_win32_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -1229,12 +1358,13 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, static cairo_int_status_t _cairo_win32_printing_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_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { cairo_win32_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -1289,11 +1419,12 @@ _cairo_win32_printing_surface_fill (void *abstract_surface, static cairo_int_status_t _cairo_win32_printing_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_win32_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -1320,6 +1451,7 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac * If we are printing a bitmap font, use fallback images to * ensure the font is not substituted. */ +#if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1341,6 +1473,7 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac if (status) return status; } +#endif return _cairo_win32_printing_surface_analyze_operation (surface, op, source); } @@ -1359,6 +1492,7 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac source = opaque; } +#if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && source->type == CAIRO_PATTERN_TYPE_SOLID) { @@ -1413,7 +1547,8 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac status = _cairo_win32_surface_show_glyphs (surface, op, source, glyphs, num_glyphs, scaled_font, - remaining_glyphs); + remaining_glyphs, + extents); if (surface->has_ctm) cairo_scaled_font_destroy (scaled_font); @@ -1422,6 +1557,7 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac return status; } +#endif SaveDC (surface->dc); old_ctm = surface->ctm; @@ -1588,6 +1724,7 @@ cairo_win32_printing_surface_create (HDC hdc) surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; _cairo_win32_printing_surface_init_ps_mode (surface); + _cairo_win32_printing_surface_init_image_support (surface); _cairo_surface_init (&surface->base, &cairo_win32_printing_surface_backend, CAIRO_CONTENT_COLOR_ALPHA); @@ -1621,6 +1758,8 @@ static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ _cairo_win32_printing_surface_show_page, NULL, /* set_clip_region */ diff --git a/src/cairo-win32-private.h b/src/cairo-win32-private.h index 21b04be..40ed20e 100644 --- a/src/cairo-win32-private.h +++ b/src/cairo-win32-private.h @@ -117,6 +117,12 @@ enum { /* Whether we can use GradientFill rectangles with this surface */ CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), }; cairo_status_t @@ -141,11 +147,12 @@ _cairo_win32_flags_for_dc (HDC dc); cairo_int_status_t _cairo_win32_surface_show_glyphs (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs); + int *remaining_glyphs, + cairo_rectangle_int_t *extents); cairo_surface_t * _cairo_win32_surface_create_similar (void *abstract_src, @@ -156,6 +163,7 @@ _cairo_win32_surface_create_similar (void *abstract_src, cairo_status_t _cairo_win32_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c index 4327621..389515b 100644 --- a/src/cairo-win32-surface.c +++ b/src/cairo-win32-surface.c @@ -431,6 +431,7 @@ _cairo_win32_surface_create_similar (void *abstract_src, cairo_status_t _cairo_win32_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -444,7 +445,7 @@ _cairo_win32_surface_clone_similar (void *abstract_surface, cairo_status_t status; cairo_surface_pattern_t pattern; - src_content = cairo_surface_get_content(src); + src_content = src->content & content; new_surface = _cairo_win32_surface_create_similar_internal (abstract_surface, src_content, @@ -877,8 +878,8 @@ _cairo_win32_surface_composite_inner (cairo_win32_surface_t *src, static cairo_int_status_t _cairo_win32_surface_composite (cairo_operator_t op, - cairo_pattern_t *pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *pattern, + const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -1461,37 +1462,36 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, /* Then combine any new region with it */ if (region) { cairo_rectangle_int_t extents; - cairo_box_int_t *boxes; - int num_boxes; + int num_rects; RGNDATA *data; size_t data_size; RECT *rects; int i; HRGN gdi_region; + cairo_rectangle_int_t rect0; /* Create a GDI region for the cairo region */ - _cairo_region_get_extents (region, &extents); - status = _cairo_region_get_boxes (region, &num_boxes, &boxes); - if (status) - return status; - - if (num_boxes == 1 && - boxes[0].p1.x == 0 && - boxes[0].p1.y == 0 && - boxes[0].p2.x == surface->extents.width && - boxes[0].p2.y == surface->extents.height) + cairo_region_get_extents (region, &extents); + num_rects = cairo_region_num_rectangles (region); + + if (num_rects == 1) + cairo_region_get_rectangle (region, 0, &rect0); + + if (num_rects == 1 && + rect0.x == 0 && + rect0.y == 0 && + rect0.width == surface->extents.width && + rect0.width == surface->extents.height) { gdi_region = NULL; - + SelectClipRgn (surface->dc, NULL); IntersectClipRect (surface->dc, - boxes[0].p1.x, - boxes[0].p1.y, - boxes[0].p2.x, - boxes[0].p2.y); - - _cairo_region_boxes_fini (region, boxes); + rect0.x, + rect0.y, + rect0.x + rect0.width, + rect0.y + rect0.height); } else { /* XXX see notes in _cairo_win32_save_initial_clip -- * this code will interact badly with a HDC which had an initial @@ -1500,31 +1500,31 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, * logical units (unlike IntersectClipRect). */ - data_size = sizeof (RGNDATAHEADER) + num_boxes * sizeof (RECT); + data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); data = malloc (data_size); - if (!data) { - _cairo_region_boxes_fini (region, boxes); + if (!data) return _cairo_error(CAIRO_STATUS_NO_MEMORY); - } rects = (RECT *)data->Buffer; data->rdh.dwSize = sizeof (RGNDATAHEADER); data->rdh.iType = RDH_RECTANGLES; - data->rdh.nCount = num_boxes; - data->rdh.nRgnSize = num_boxes * sizeof (RECT); + data->rdh.nCount = num_rects; + data->rdh.nRgnSize = num_rects * sizeof (RECT); data->rdh.rcBound.left = extents.x; data->rdh.rcBound.top = extents.y; data->rdh.rcBound.right = extents.x + extents.width; data->rdh.rcBound.bottom = extents.y + extents.height; - for (i = 0; i < num_boxes; i++) { - rects[i].left = boxes[i].p1.x; - rects[i].top = boxes[i].p1.y; - rects[i].right = boxes[i].p2.x; - rects[i].bottom = boxes[i].p2.y; - } + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_boxes_fini (region, boxes); + cairo_region_get_rectangle (region, i, &rect); + + rects[i].left = rect.x; + rects[i].top = rect.y; + rects[i].right = rect.x + rect.width; + rects[i].bottom = rect.y + rect.height; + } gdi_region = ExtCreateRegion (NULL, data_size, data); free (data); @@ -1565,11 +1565,12 @@ _cairo_win32_surface_flush (void *abstract_surface) cairo_int_status_t _cairo_win32_surface_show_glyphs (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { #if CAIRO_HAS_WIN32_FONT cairo_win32_surface_t *dst = surface; @@ -1980,6 +1981,8 @@ static const cairo_surface_backend_t cairo_win32_surface_backend = { _cairo_win32_surface_composite, _cairo_win32_surface_fill_rectangles, NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_win32_surface_set_clip_region, diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index c63e851..a7340cf 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -101,6 +101,8 @@ typedef struct cairo_xcb_surface { #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) +#define CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) static void _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface); @@ -611,6 +613,20 @@ _cairo_xcb_surface_acquire_source_image (void *abstract_surfa return CAIRO_STATUS_SUCCESS; } +static cairo_surface_t * +_cairo_xcb_surface_snapshot (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + return &image->base; +} + static void _cairo_xcb_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, @@ -671,6 +687,7 @@ _cairo_xcb_surface_same_screen (cairo_xcb_surface_t *dst, static cairo_status_t _cairo_xcb_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -839,13 +856,22 @@ _cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, switch (attributes->extend) { case CAIRO_EXTEND_NONE: - _cairo_xcb_surface_set_repeat (surface, 0); + _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_NONE); break; case CAIRO_EXTEND_REPEAT: - _cairo_xcb_surface_set_repeat (surface, 1); + _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_NORMAL); break; case CAIRO_EXTEND_REFLECT: + if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_REFLECT(surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_REFLECT); + break; case CAIRO_EXTEND_PAD: + if (!CAIRO_SURFACE_RENDER_HAS_REPEAT_PAD(surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_xcb_surface_set_repeat (surface, XCB_RENDER_REPEAT_PAD); + break; + default: return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -948,10 +974,10 @@ typedef enum { * hit the bug and won't be able to use a core protocol fallback. */ static composite_operation_t -_categorize_composite_operation (cairo_xcb_surface_t *dst, - cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_bool_t have_mask) +_categorize_composite_operation (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + cairo_bool_t have_mask) { #if XXX_BUGGY_REPEAT @@ -1088,8 +1114,8 @@ _render_operator (cairo_operator_t op) static cairo_int_status_t _cairo_xcb_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -1119,9 +1145,11 @@ _cairo_xcb_surface_composite (cairo_operator_t op, status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, mask_x, mask_y, width, height, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT, (cairo_surface_t **) &src, (cairo_surface_t **) &mask, &src_attr, &mask_attr); @@ -1377,7 +1405,7 @@ _create_trapezoid_mask (cairo_xcb_surface_t *dst, static cairo_int_status_t _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, @@ -1407,7 +1435,9 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_pattern_acquire_surface (pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, width, height, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT, (cairo_surface_t **) &src, &attributes); if (status) @@ -1555,37 +1585,33 @@ _cairo_xcb_surface_set_clip_region (void *abstract_surface, xcb_render_change_picture (surface->dpy, surface->dst_picture, XCB_RENDER_CP_CLIP_MASK, none); } else { - cairo_box_int_t *boxes; - cairo_status_t status; xcb_rectangle_t *rects = NULL; - int n_boxes, i; + int n_rects, i; - status = _cairo_region_get_boxes (region, &n_boxes, &boxes); - if (status) - return status; + n_rects = cairo_region_num_rectangles (region); - if (n_boxes > 0) { - rects = _cairo_malloc_ab (n_boxes, sizeof(xcb_rectangle_t)); - if (rects == NULL) { - _cairo_region_boxes_fini (region, boxes); + if (n_rects > 0) { + rects = _cairo_malloc_ab (n_rects, sizeof(xcb_rectangle_t)); + if (rects == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } } else { rects = NULL; } - for (i = 0; i < n_boxes; i++) { - rects[i].x = boxes[i].p1.x; - rects[i].y = boxes[i].p1.y; - rects[i].width = boxes[i].p2.x - boxes[i].p1.x; - rects[i].height = boxes[i].p2.y - boxes[i].p1.y; - } + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_boxes_fini (region, boxes); + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } surface->have_clip_rects = TRUE; surface->clip_rects = rects; - surface->num_clip_rects = n_boxes; + surface->num_clip_rects = n_rects; if (surface->gc) _cairo_xcb_surface_set_gc_clip_rects (surface); @@ -1622,13 +1648,14 @@ _cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font); static cairo_int_status_t -_cairo_xcb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - int *remaining_glyphs); +_cairo_xcb_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + int *remaining_glyphs, + cairo_rectangle_int_t *extents); static cairo_bool_t _cairo_xcb_surface_is_similar (void *surface_a, @@ -1683,6 +1710,8 @@ static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_composite, _cairo_xcb_surface_fill_rectangles, _cairo_xcb_surface_composite_trapezoids, + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_xcb_surface_set_clip_region, @@ -1700,7 +1729,8 @@ static const cairo_surface_backend_t cairo_xcb_surface_backend = { NULL, /* stroke */ NULL, /* fill */ _cairo_xcb_surface_show_glyphs, - NULL, /* snapshot */ + + _cairo_xcb_surface_snapshot, _cairo_xcb_surface_is_similar, @@ -2427,13 +2457,14 @@ _cairo_xcb_surface_emit_glyphs (cairo_xcb_surface_t *dst, } static cairo_int_status_t -_cairo_xcb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) +_cairo_xcb_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_xcb_surface_t *dst = abstract_dst; @@ -2500,7 +2531,9 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, 0, 0, 1, 1, + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attributes); } else { @@ -2514,8 +2547,10 @@ _cairo_xcb_surface_show_glyphs (void *abstract_dst, goto BAIL; status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, glyph_extents.x, glyph_extents.y, glyph_extents.width, glyph_extents.height, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT, (cairo_surface_t **) &src, &attributes); } diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c index b2e2755..65df2b7 100644 --- a/src/cairo-xlib-display.c +++ b/src/cairo-xlib-display.c @@ -38,8 +38,6 @@ #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" -#include <fontconfig/fontconfig.h> - #include <X11/Xlibint.h> /* For XESetCloseDisplay */ typedef int (*cairo_xlib_error_func_t) (Display *display, @@ -208,13 +206,15 @@ _cairo_xlib_close_display (Display *dpy, XExtCodes *codes) return 0; } -cairo_xlib_display_t * -_cairo_xlib_display_get (Display *dpy) +cairo_status_t +_cairo_xlib_display_get (Display *dpy, + cairo_xlib_display_t **out) { cairo_xlib_display_t *display; cairo_xlib_display_t **prev; XExtCodes *codes; - int major_unused, minor_unused; + int render_major, render_minor; + cairo_status_t status = CAIRO_STATUS_SUCCESS; /* There is an apparent deadlock between this mutex and the * mutex for the display, but it's actually safe. For the @@ -246,8 +246,8 @@ _cairo_xlib_display_get (Display *dpy) } display = malloc (sizeof (cairo_xlib_display_t)); - if (display == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + if (unlikely (display == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNLOCK; } @@ -257,11 +257,11 @@ _cairo_xlib_display_get (Display *dpy) * add our hook. For now, that means Render, so we call into its * QueryVersion function to ensure it gets initialized. */ - XRenderQueryVersion (dpy, &major_unused, &minor_unused); + XRenderQueryVersion (dpy, &render_major, &render_minor); codes = XAddExtension (dpy); - if (codes == NULL) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + if (unlikely (codes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); free (display); display = NULL; goto UNLOCK; @@ -279,10 +279,13 @@ _cairo_xlib_display_get (Display *dpy) display->close_display_hooks = NULL; display->closed = FALSE; + display->render_major = render_major; + display->render_minor = render_minor; memset (display->cached_xrender_formats, 0, sizeof (display->cached_xrender_formats)); display->buggy_repeat = FALSE; + display->buggy_pad_reflect = TRUE; /* This buggy_repeat condition is very complicated because there * are multiple X server code bases (with multiple versioning @@ -329,13 +332,19 @@ _cairo_xlib_display_get (Display *dpy) * (just using VendorRelase < 70000000), as buggy_repeat=TRUE. */ if (strstr (ServerVendor (dpy), "X.Org") != NULL) { - if (VendorRelease (dpy) >= 60700000 && VendorRelease (dpy) < 70000000) - display->buggy_repeat = TRUE; - if (VendorRelease (dpy) < 10400000) - display->buggy_repeat = TRUE; + if (VendorRelease (dpy) >= 60700000) { + if (VendorRelease (dpy) < 70000000) + display->buggy_repeat = TRUE; + } else { + if (VendorRelease (dpy) < 10400000) + display->buggy_repeat = TRUE; + if (VendorRelease (dpy) >= 10699000) + display->buggy_pad_reflect = FALSE; + } } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { if (VendorRelease (dpy) <= 40500000) display->buggy_repeat = TRUE; + } display->next = _cairo_xlib_display_list; @@ -343,7 +352,8 @@ _cairo_xlib_display_get (Display *dpy) UNLOCK: CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); - return display; + *out = display; + return status; } void diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h index e381e58..4995bc6 100644 --- a/src/cairo-xlib-private.h +++ b/src/cairo-xlib-private.h @@ -64,6 +64,8 @@ struct _cairo_xlib_display { Display *display; cairo_xlib_screen_info_t *screens; + int render_major; + int render_minor; XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_A1 + 1]; cairo_xlib_job_t *workqueue; @@ -71,6 +73,7 @@ struct _cairo_xlib_display { cairo_xlib_hook_t *close_display_hooks; unsigned int buggy_repeat :1; + unsigned int buggy_pad_reflect :1; unsigned int closed :1; }; @@ -97,6 +100,7 @@ struct _cairo_xlib_screen_info { Screen *screen; cairo_bool_t has_render; + cairo_bool_t has_font_options; cairo_font_options_t font_options; GC gc[9]; @@ -105,8 +109,8 @@ struct _cairo_xlib_screen_info { cairo_array_t visuals; }; -cairo_private cairo_xlib_display_t * -_cairo_xlib_display_get (Display *display); +cairo_private cairo_status_t +_cairo_xlib_display_get (Display *display, cairo_xlib_display_t **out); cairo_private cairo_xlib_display_t * _cairo_xlib_display_reference (cairo_xlib_display_t *info); @@ -135,8 +139,10 @@ cairo_private XRenderPictFormat * _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, cairo_format_t format); -cairo_private cairo_xlib_screen_info_t * -_cairo_xlib_screen_info_get (cairo_xlib_display_t *display, Screen *screen); +cairo_private cairo_status_t +_cairo_xlib_screen_info_get (cairo_xlib_display_t *display, + Screen *screen, + cairo_xlib_screen_info_t **out); cairo_private cairo_xlib_screen_info_t * _cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info); @@ -147,9 +153,18 @@ cairo_private void _cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info); cairo_private GC -_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, int depth); +_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, + int depth, + unsigned int *need_reset); + cairo_private cairo_status_t -_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip); +_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, + int depth, + GC gc, + cairo_bool_t reset_clip); + +cairo_private cairo_font_options_t * +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_info_t *info); cairo_private cairo_status_t _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c index f670b8a..8f1e949 100644 --- a/src/cairo-xlib-screen.c +++ b/src/cairo-xlib-screen.c @@ -57,6 +57,8 @@ #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" +#include "cairo-xlib-surface-private.h" + #include <fontconfig/fontconfig.h> static int @@ -110,7 +112,7 @@ get_integer_default (Display *dpy, v = XGetDefault (dpy, "Xft", option); if (v) { -#if CAIRO_HAS_FT_FONT +#if CAIRO_HAS_FC_FONT if (FcNameConstant ((FcChar8 *) v, value)) return TRUE; #endif @@ -123,7 +125,15 @@ get_integer_default (Display *dpy, return FALSE; } -/* Old versions of fontconfig didn't have these options */ +#ifndef FC_RGBA_UNKNOWN +#define FC_RGBA_UNKNOWN 0 +#define FC_RGBA_RGB 1 +#define FC_RGBA_BGR 2 +#define FC_RGBA_VRGB 3 +#define FC_RGBA_VBGR 4 +#define FC_RGBA_NONE 5 +#endif + #ifndef FC_HINT_NONE #define FC_HINT_NONE 0 #define FC_HINT_SLIGHT 1 @@ -131,20 +141,10 @@ get_integer_default (Display *dpy, #define FC_HINT_FULL 3 #endif -/* Fontconfig version older than 2.6 didn't have these options */ -#ifndef FC_LCD_FILTER -#define FC_LCD_FILTER "lcdfilter" -#endif -/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */ -#ifndef FC_LCD_NONE -#define FC_LCD_NONE 0 -#define FC_LCD_DEFAULT 1 -#define FC_LCD_LIGHT 2 -#define FC_LCD_LEGACY 3 -#endif static void -_cairo_xlib_init_screen_font_options (Display *dpy, cairo_xlib_screen_info_t *info) +_cairo_xlib_init_screen_font_options (Display *dpy, + cairo_xlib_screen_info_t *info) { cairo_bool_t xft_hinting; cairo_bool_t xft_antialias; @@ -317,15 +317,17 @@ _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info) free (info); } -cairo_xlib_screen_info_t * -_cairo_xlib_screen_info_get (cairo_xlib_display_t *display, Screen *screen) +cairo_status_t +_cairo_xlib_screen_info_get (cairo_xlib_display_t *display, + Screen *screen, + cairo_xlib_screen_info_t **out) { cairo_xlib_screen_info_t *info = NULL, **prev; CAIRO_MUTEX_LOCK (display->mutex); if (display->closed) { CAIRO_MUTEX_UNLOCK (display->mutex); - return NULL; + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); } for (prev = &display->screens; (info = *prev); prev = &(*prev)->next) { @@ -347,36 +349,41 @@ _cairo_xlib_screen_info_get (cairo_xlib_display_t *display, Screen *screen) info = _cairo_xlib_screen_info_reference (info); } else { info = malloc (sizeof (cairo_xlib_screen_info_t)); - if (info != NULL) { - CAIRO_REFERENCE_COUNT_INIT (&info->ref_count, 2); /* Add one for display cache */ - CAIRO_MUTEX_INIT (info->mutex); - info->display = _cairo_xlib_display_reference (display); - info->screen = screen; - info->has_render = FALSE; - _cairo_font_options_init_default (&info->font_options); - memset (info->gc, 0, sizeof (info->gc)); - info->gc_needs_clip_reset = 0; - - _cairo_array_init (&info->visuals, - sizeof (cairo_xlib_visual_info_t*)); - - if (screen) { - Display *dpy = display->display; - int event_base, error_base; - - info->has_render = (XRenderQueryExtension (dpy, &event_base, &error_base) && - (XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0)); - _cairo_xlib_init_screen_font_options (dpy, info); - } - - CAIRO_MUTEX_LOCK (display->mutex); - info->next = display->screens; - display->screens = info; - CAIRO_MUTEX_UNLOCK (display->mutex); + if (unlikely (info == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + CAIRO_REFERENCE_COUNT_INIT (&info->ref_count, 2); /* Add one for display cache */ + CAIRO_MUTEX_INIT (info->mutex); + info->display = _cairo_xlib_display_reference (display); + info->screen = screen; + info->has_render = FALSE; + info->has_font_options = FALSE; + memset (info->gc, 0, sizeof (info->gc)); + info->gc_needs_clip_reset = 0; + + _cairo_array_init (&info->visuals, + sizeof (cairo_xlib_visual_info_t*)); + + if (screen) { + Display *dpy = display->display; + int event_base, error_base; + + info->has_render = (XRenderQueryExtension (dpy, &event_base, &error_base) && + (XRenderFindVisualFormat (dpy, DefaultVisual (dpy, DefaultScreen (dpy))) != 0)); } + + /* Small window of opportunity for two screen infos for the same + * Screen - just wastes a little bit of memory but should not cause + * any corruption. + */ + CAIRO_MUTEX_LOCK (display->mutex); + info->next = display->screens; + display->screens = info; + CAIRO_MUTEX_UNLOCK (display->mutex); } - return info; + *out = info; + return CAIRO_STATUS_SUCCESS; } static int @@ -396,7 +403,9 @@ depth_to_index (int depth) } GC -_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, int depth) +_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, + int depth, + unsigned int *dirty) { GC gc; cairo_bool_t needs_reset; @@ -411,7 +420,7 @@ _cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info, int depth) CAIRO_MUTEX_UNLOCK (info->mutex); if (needs_reset) - XSetClipMask(info->display->display, gc, None); + *dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC; return gc; } @@ -473,7 +482,7 @@ _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, XScreenNumberOfScreen (info->screen), visual->visualid, &ret); - if (status) + if (unlikely (status)) return status; CAIRO_MUTEX_LOCK (info->mutex); @@ -494,7 +503,7 @@ _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, status = _cairo_array_append (&info->visuals, &ret); CAIRO_MUTEX_UNLOCK (info->mutex); - if (status) { + if (unlikely (status)) { _cairo_xlib_visual_info_destroy (dpy, ret); return status; } @@ -502,3 +511,25 @@ _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info, *out = ret; return CAIRO_STATUS_SUCCESS; } + +cairo_font_options_t * +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_info_t *info) +{ + if (info->has_font_options) + return &info->font_options; + + CAIRO_MUTEX_LOCK (info->mutex); + if (! info->has_font_options) { + Display *dpy = info->display->display; + + _cairo_font_options_init_default (&info->font_options); + + if (info->screen != NULL) + _cairo_xlib_init_screen_font_options (dpy, info); + + info->has_font_options = TRUE; + } + CAIRO_MUTEX_UNLOCK (info->mutex); + + return &info->font_options; +} diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h index e06fd97..164fe15 100644 --- a/src/cairo-xlib-surface-private.h +++ b/src/cairo-xlib-surface-private.h @@ -76,6 +76,7 @@ struct _cairo_xlib_surface { * we can reuse the test for now. */ cairo_bool_t buggy_repeat; + cairo_bool_t buggy_pad_reflect; int width; int height; @@ -85,7 +86,8 @@ struct _cairo_xlib_surface { unsigned int clip_dirty; cairo_bool_t have_clip_rects; - XRectangle embedded_clip_rects[4]; + cairo_bool_t gc_has_clip_rects; + XRectangle embedded_clip_rects[8]; XRectangle *clip_rects; int num_clip_rects; diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index ba38f5d..67b4ca6 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -82,11 +82,12 @@ _native_byte_order_lsb (void); static cairo_int_status_t _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, - cairo_pattern_t *src_pattern, + const cairo_pattern_t *src_pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs); + int *remaining_glyphs, + cairo_rectangle_int_t *extents); /* * Instead of taking two round trips for each blending request, @@ -135,8 +136,7 @@ _cairo_xlib_surface_create_similar_with_format (void *abstract_src, cairo_xlib_surface_t *surface; XRenderPictFormat *xrender_format; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return NULL; + assert (width <= XLIB_COORD_MAX && height <= XLIB_COORD_MAX); /* As a good first approximation, if the display doesn't have even * the most elementary RENDER operation, then we're better off @@ -146,9 +146,8 @@ _cairo_xlib_surface_create_similar_with_format (void *abstract_src, if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) return NULL; - xrender_format = _cairo_xlib_display_get_xrender_format ( - src->display, - format); + xrender_format = _cairo_xlib_display_get_xrender_format (src->display, + format); if (xrender_format == NULL) return NULL; @@ -209,7 +208,7 @@ _cairo_xlib_surface_create_similar (void *abstract_src, Pixmap pix; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (_cairo_error(CAIRO_STATUS_NO_MEMORY)); + return NULL; _cairo_xlib_display_notify (src->display); @@ -306,7 +305,7 @@ _cairo_xlib_surface_finish (void *abstract_surface) status2 = _cairo_xlib_screen_put_gc (surface->screen_info, surface->depth, surface->gc, - surface->have_clip_rects); + surface->gc_has_clip_rects); surface->gc = NULL; if (status == CAIRO_STATUS_SUCCESS) status = status2; @@ -669,8 +668,9 @@ _get_image_surface (cairo_xlib_surface_t *surface, * temporary pixmap */ Pixmap pixmap; - cairo_status_t status = _cairo_xlib_surface_ensure_gc (surface); - if (status) + + status = _cairo_xlib_surface_ensure_gc (surface); + if (unlikely (status)) return status; pixmap = XCreatePixmap (surface->dpy, @@ -715,7 +715,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, ximage->height, ximage->bytes_per_line); status = image->base.status; - if (status) + if (unlikely (status)) goto BAIL; /* Let the surface take ownership of the data */ @@ -776,14 +776,14 @@ _get_image_surface (cairo_xlib_surface_t *surface, status = _cairo_xlib_screen_get_visual_info (surface->screen_info, surface->visual, &visual_info); - if (status) + if (unlikely (status)) goto BAIL; } image = (cairo_image_surface_t *) cairo_image_surface_create (format, ximage->width, ximage->height); status = image->base.status; - if (status) + if (unlikely (status)) goto BAIL; data = cairo_image_surface_get_data (&image->base); @@ -820,7 +820,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, BAIL: XDestroyImage (ximage); - if (status) { + if (unlikely (status)) { if (image) { cairo_surface_destroy (&image->base); image = NULL; @@ -869,6 +869,7 @@ _cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_surface_t *surface) static void _cairo_xlib_surface_set_gc_clip_rects (cairo_xlib_surface_t *surface) { + surface->gc_has_clip_rects = surface->have_clip_rects; if (surface->have_clip_rects) { XSetClipRectangles(surface->dpy, surface->gc, 0, 0, @@ -888,8 +889,9 @@ _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t *surface) surface->drawable, surface->xrender_format, 0, NULL); - _cairo_xlib_surface_set_picture_clip_rects (surface); - } else if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) + } + + if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) _cairo_xlib_surface_set_picture_clip_rects (surface); } @@ -900,17 +902,18 @@ _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) if (surface->gc == NULL) { surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info, - surface->depth); + surface->depth, + &surface->clip_dirty); if (surface->gc == NULL) { gcv.graphics_exposures = False; surface->gc = XCreateGC (surface->dpy, surface->drawable, GCGraphicsExposures, &gcv); - if (!surface->gc) + if (unlikely (surface->gc == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } + } - _cairo_xlib_surface_set_gc_clip_rects (surface); - } else if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC) + if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC) _cairo_xlib_surface_set_gc_clip_rects (surface); return CAIRO_STATUS_SUCCESS; @@ -947,9 +950,10 @@ _draw_image_surface (cairo_xlib_surface_t *surface, ximage.blue_mask = surface->b_mask; ximage.xoffset = 0; - if (image_masks.red_mask == surface->r_mask && - image_masks.green_mask == surface->g_mask && - image_masks.blue_mask == surface->b_mask) + if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && + (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && + (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && + (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) { int ret; @@ -985,7 +989,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, ximage.bits_per_pixel); ximage.bytes_per_line = stride; ximage.data = _cairo_malloc_ab (stride, ximage.height); - if (ximage.data == NULL) + if (unlikely (ximage.data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); own_data = TRUE; @@ -1009,12 +1013,12 @@ _draw_image_surface (cairo_xlib_surface_t *surface, status = _cairo_xlib_screen_get_visual_info (surface->screen_info, surface->visual, &visual_info); - if (status) + if (unlikely (status)) goto BAIL; } - rowstride = cairo_image_surface_get_stride (&image->base) >> 2; - row = (uint32_t *) cairo_image_surface_get_data (&image->base); + rowstride = image->stride >> 2; + row = (uint32_t *) image->data; x0 = dst_x + surface->base.device_transform.x0; y0 = dst_y + surface->base.device_transform.y0; for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); @@ -1066,7 +1070,7 @@ _draw_image_surface (cairo_xlib_surface_t *surface, } status = _cairo_xlib_surface_ensure_gc (surface); - if (status) + if (unlikely (status)) goto BAIL; XPutImage(surface->dpy, surface->drawable, surface->gc, @@ -1092,7 +1096,7 @@ _cairo_xlib_surface_acquire_source_image (void *abstract_surf _cairo_xlib_display_notify (surface->display); status = _get_image_surface (surface, NULL, &image, NULL); - if (status) + if (unlikely (status)) return status; *image_out = image; @@ -1101,6 +1105,22 @@ _cairo_xlib_surface_acquire_source_image (void *abstract_surf return CAIRO_STATUS_SUCCESS; } +static cairo_surface_t * +_cairo_xlib_surface_snapshot (void *abstract_surface) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + _cairo_xlib_display_notify (surface->display); + + status = _get_image_surface (surface, NULL, &image, NULL); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + return &image->base; +} + static void _cairo_xlib_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, @@ -1123,7 +1143,7 @@ _cairo_xlib_surface_acquire_dest_image (void *abstract_surfac _cairo_xlib_display_notify (surface->display); status = _get_image_surface (surface, interest_rect, &image, image_rect_out); - if (status) + if (unlikely (status)) return status; *image_out = image; @@ -1165,6 +1185,7 @@ _cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, static cairo_status_t _cairo_xlib_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -1191,16 +1212,20 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - - if (! CAIRO_FORMAT_VALID (image_src->format)) - return CAIRO_INT_STATUS_UNSUPPORTED; + cairo_format_t format; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return CAIRO_INT_STATUS_UNSUPPORTED; + format = image_src->format; + if (format == CAIRO_FORMAT_INVALID || + (_cairo_content_from_format (format) & ~content)) + { + format = _cairo_format_from_content (image_src->base.content & content); + } clone = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_similar_with_format (surface, - image_src->format, + format, width, height); if (clone == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1212,7 +1237,7 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, src_x, src_y, width, height, 0, 0); - if (status) { + if (unlikely (status)) { cairo_surface_destroy (&clone->base); return status; } @@ -1229,7 +1254,7 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, static cairo_surface_t * _cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface, - cairo_solid_pattern_t *solid_pattern) + const cairo_solid_pattern_t *solid_pattern) { /* This function's only responsibility is to create a proper surface * for when XRender is not available. The proper surface is a xlib @@ -1259,7 +1284,7 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac _cairo_image_surface_create_with_content (solid_pattern->content, width, height); status = image->base.status; - if (status) + if (unlikely (status)) goto BAIL; pixmap = XCreatePixmap (other->dpy, @@ -1275,26 +1300,26 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac width, height, other->depth); status = surface->base.status; - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, - &solid_pattern->base); - if (status) + &solid_pattern->base, NULL); + if (unlikely (status)) goto BAIL; status = _draw_image_surface (surface, image, 0, 0, width, height, 0, 0); - if (status) + if (unlikely (status)) goto BAIL; BAIL: cairo_surface_destroy (&image->base); - if (status) { + if (unlikely (status)) { if (pixmap != None) XFreePixmap (other->dpy, pixmap); cairo_surface_destroy (&surface->base); @@ -1306,26 +1331,39 @@ _cairo_xlib_surface_create_solid_pattern_surface (void *abstrac return &surface->base; } +static cairo_bool_t +_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, + const cairo_solid_pattern_t *solid_pattern) +{ + cairo_xlib_surface_t *other = abstract_surface; + return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); +} + + static cairo_status_t _cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, - cairo_matrix_t *matrix) + cairo_matrix_t *matrix, + double xc, + double yc) { XTransform xtransform; if (!surface->src_picture) return CAIRO_STATUS_SUCCESS; - + /* Casting between pixman_transform_t and XTransform is safe because * they happen to be the exact same type. */ - _cairo_matrix_to_pixman_matrix (matrix, (pixman_transform_t *)&xtransform); + _cairo_matrix_to_pixman_matrix (matrix, + (pixman_transform_t *) &xtransform, + xc, yc); if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) return CAIRO_STATUS_SUCCESS; if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) return CAIRO_INT_STATUS_UNSUPPORTED; - + XRenderSetPictureTransform (surface->dpy, surface->src_picture, &xtransform); surface->xtransform = xtransform; @@ -1407,14 +1445,17 @@ _cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) static cairo_int_status_t _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, - cairo_surface_attributes_t *attributes) + cairo_surface_attributes_t *attributes, + double xc, + double yc) { cairo_int_status_t status; _cairo_xlib_surface_ensure_src_picture (surface); - status = _cairo_xlib_surface_set_matrix (surface, &attributes->matrix); - if (status) + status = _cairo_xlib_surface_set_matrix (surface, &attributes->matrix, + xc, yc); + if (unlikely (status)) return status; switch (attributes->extend) { @@ -1425,13 +1466,21 @@ _cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, _cairo_xlib_surface_set_repeat (surface, RepeatNormal); break; case CAIRO_EXTEND_REFLECT: + if (surface->buggy_pad_reflect) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_xlib_surface_set_repeat (surface, RepeatReflect); + break; case CAIRO_EXTEND_PAD: + if (surface->buggy_pad_reflect) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_xlib_surface_set_repeat (surface, RepeatPad); + break; default: return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_xlib_surface_set_filter (surface, attributes->filter); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -1487,14 +1536,15 @@ _surface_has_alpha (cairo_xlib_surface_t *surface) */ static cairo_bool_t _operator_needs_alpha_composite (cairo_operator_t op, - cairo_bool_t surface_has_alpha) + cairo_bool_t destination_has_alpha, + cairo_bool_t source_has_alpha) { if (op == CAIRO_OPERATOR_SOURCE || - (!surface_has_alpha && + (! source_has_alpha && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ATOP || op == CAIRO_OPERATOR_IN))) - return FALSE; + return destination_has_alpha; return TRUE; } @@ -1531,7 +1581,7 @@ typedef enum { static composite_operation_t _categorize_composite_operation (cairo_xlib_surface_t *dst, cairo_operator_t op, - cairo_pattern_t *src_pattern, + const cairo_pattern_t *src_pattern, cairo_bool_t have_mask) { @@ -1603,7 +1653,9 @@ _recategorize_composite_operation (cairo_xlib_surface_t *dst, return DO_UNSUPPORTED; needs_alpha_composite = - _operator_needs_alpha_composite (op, _surface_has_alpha (src)); + _operator_needs_alpha_composite (op, + _surface_has_alpha (dst), + _surface_has_alpha (src)); if (! have_mask && is_integer_translation && @@ -1680,8 +1732,8 @@ _render_operator (cairo_operator_t op) static cairo_int_status_t _cairo_xlib_surface_composite (cairo_operator_t op, - cairo_pattern_t *src_pattern, - cairo_pattern_t *mask_pattern, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -1700,6 +1752,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op, composite_operation_t operation; int itx, ity; cairo_bool_t is_integer_translation; + cairo_bool_t needs_alpha_composite; + cairo_content_t src_content; _cairo_xlib_display_notify (dst->display); @@ -1708,15 +1762,27 @@ _cairo_xlib_surface_composite (cairo_operator_t op, if (operation == DO_UNSUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; + needs_alpha_composite = + _operator_needs_alpha_composite (op, + _surface_has_alpha (dst), + ! _cairo_pattern_is_opaque (src_pattern)); + src_content = CAIRO_CONTENT_COLOR_ALPHA; + if (! needs_alpha_composite) + src_content &= ~CAIRO_CONTENT_ALPHA; + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, &dst->base, + src_content, src_x, src_y, mask_x, mask_y, width, height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, (cairo_surface_t **) &mask, &src_attr, &mask_attr); - if (status) + if (unlikely (status)) return status; /* check for fallback surfaces that we cannot handle ... */ @@ -1742,14 +1808,18 @@ _cairo_xlib_surface_composite (cairo_operator_t op, switch (operation) { case DO_RENDER: - status = _cairo_xlib_surface_set_attributes (src, &src_attr); - if (status) + status = _cairo_xlib_surface_set_attributes (src, &src_attr, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) goto BAIL; _cairo_xlib_surface_ensure_dst_picture (dst); if (mask) { - status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); - if (status) + status = _cairo_xlib_surface_set_attributes (mask, &mask_attr, + dst_x + width / 2., + dst_y + height/ 2.); + if (unlikely (status)) goto BAIL; XRenderComposite (dst->dpy, @@ -1780,7 +1850,7 @@ _cairo_xlib_surface_composite (cairo_operator_t op, case DO_XCOPYAREA: status = _cairo_xlib_surface_ensure_gc (dst); - if (status) + if (unlikely (status)) goto BAIL; is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, @@ -1808,7 +1878,7 @@ _cairo_xlib_surface_composite (cairo_operator_t op, */ status = _cairo_xlib_surface_ensure_gc (dst); - if (status) + if (unlikely (status)) goto BAIL; is_integer_translation = _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); @@ -1863,16 +1933,18 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, _cairo_pattern_init_solid (&solid, color, CAIRO_CONTENT_COLOR); status = _cairo_xlib_surface_ensure_gc (surface); - if (status) + if (unlikely (status)) return status; status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, 0, 0, ARRAY_LENGTH (dither_pattern[0]), ARRAY_LENGTH (dither_pattern), + CAIRO_PATTERN_ACQUIRE_NONE, &solid_surface, &attrs); - if (status) + if (unlikely (status)) return status; if (! _cairo_surface_is_xlib (solid_surface)) { @@ -1908,8 +1980,6 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, { cairo_xlib_surface_t *surface = abstract_surface; XRenderColor render_color; - XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; - XRectangle *xrects = static_xrects; int i; _cairo_xlib_display_notify (surface->display); @@ -1931,27 +2001,44 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; - if (num_rects > ARRAY_LENGTH (static_xrects)) { - xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); - if (xrects == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + _cairo_xlib_surface_ensure_dst_picture (surface); + if (num_rects == 1) { + /* Take advantage of the protocol compaction that libXrender performs + * to amalgamate sequences of XRenderFillRectangle(). + */ + XRenderFillRectangle (surface->dpy, + _render_operator (op), + surface->dst_picture, + &render_color, + rects->x, + rects->y, + rects->width, + rects->height); + } else { + XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *xrects = static_xrects; - for (i = 0; i < num_rects; i++) { - xrects[i].x = rects[i].x; - xrects[i].y = rects[i].y; - xrects[i].width = rects[i].width; - xrects[i].height = rects[i].height; - } + if (num_rects > ARRAY_LENGTH (static_xrects)) { + xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); + if (unlikely (xrects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } - _cairo_xlib_surface_ensure_dst_picture (surface); - XRenderFillRectangles (surface->dpy, - _render_operator (op), - surface->dst_picture, - &render_color, xrects, num_rects); + for (i = 0; i < num_rects; i++) { + xrects[i].x = rects[i].x; + xrects[i].y = rects[i].y; + xrects[i].width = rects[i].width; + xrects[i].height = rects[i].height; + } - if (xrects != static_xrects) - free (xrects); + XRenderFillRectangles (surface->dpy, + _render_operator (op), + surface->dst_picture, + &render_color, xrects, num_rects); + + if (xrects != static_xrects) + free (xrects); + } return CAIRO_STATUS_SUCCESS; } @@ -2071,7 +2158,7 @@ _create_trapezoid_mask (cairo_xlib_surface_t *dst, static cairo_int_status_t _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *abstract_dst, cairo_antialias_t antialias, int src_x, @@ -2102,10 +2189,14 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_pattern_acquire_surface (pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, src_x, src_y, width, height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attributes); - if (status) + if (unlikely (status)) return status; operation = _recategorize_composite_operation (dst, op, src, @@ -2143,8 +2234,10 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, render_src_y = src_y + render_reference_y - dst_y; _cairo_xlib_surface_ensure_dst_picture (dst); - status = _cairo_xlib_surface_set_attributes (src, &attributes); - if (status) + status = _cairo_xlib_surface_set_attributes (src, &attributes, + dst_x + width / 2., + dst_y + height / 2.); + if (unlikely (status)) goto BAIL; if (!_cairo_operator_bounded_by_mask (op)) { @@ -2192,7 +2285,7 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, if (num_traps > ARRAY_LENGTH (xtraps_stack)) { xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); - if (xtraps == NULL) { + if (unlikely (xtraps == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } @@ -2229,6 +2322,32 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, return status; } +static cairo_region_t * +_surface_maybe_clip_region (cairo_xlib_surface_t *surface, + cairo_region_t *clip, + cairo_region_t *bounded) +{ + cairo_rectangle_int_t rect; + + cairo_region_get_extents (clip, &rect); + if (rect.x >= 0 && + rect.y >= 0 && + rect.x + rect.width <= surface->width && + rect.y + rect.height <= surface->height) + { + return clip; + } + + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + _cairo_region_init_rectangle (bounded, &rect); + + bounded->status = cairo_region_intersect (bounded, clip); + + return bounded; +} + static cairo_int_status_t _cairo_xlib_surface_set_clip_region (void *abstract_surface, cairo_region_t *region) @@ -2248,66 +2367,50 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, surface->num_clip_rects = 0; if (region != NULL) { - cairo_box_int_t *boxes; - cairo_status_t status; XRectangle *rects = NULL; - int n_boxes, i; - cairo_rectangle_int_t rect; - cairo_region_t bound, bounded; - - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; + int n_rects, i; + cairo_region_t bounded; /* Intersect the region with the bounds of the surface. This * is necessary so we don't wrap around when we convert cairo's * 32 bit region into 16 bit rectangles. */ - _cairo_region_init_rect (&bound, &rect); - _cairo_region_init (&bounded); - status = _cairo_region_intersect (&bounded, &bound, region); - if (status) { - _cairo_region_fini (&bound); - _cairo_region_fini (&bounded); - return status; - } - - status = _cairo_region_get_boxes (&bounded, &n_boxes, &boxes); - if (status) { - _cairo_region_fini (&bound); - _cairo_region_fini (&bounded); - return status; - } - - if (n_boxes > ARRAY_LENGTH (surface->embedded_clip_rects)) { - rects = _cairo_malloc_ab (n_boxes, sizeof (XRectangle)); - if (rects == NULL) { - _cairo_region_boxes_fini (&bounded, boxes); - _cairo_region_fini (&bound); - _cairo_region_fini (&bounded); + region = _surface_maybe_clip_region (surface, region, &bounded); + if (unlikely (region->status)) + return region->status; + + n_rects = cairo_region_num_rectangles (region); + if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); + if (unlikely (rects == NULL)) { + if (unlikely (region == &bounded)) + _cairo_region_fini (&bounded); return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + } } else { rects = surface->embedded_clip_rects; } - for (i = 0; i < n_boxes; i++) { - rects[i].x = boxes[i].p1.x; - rects[i].y = boxes[i].p1.y; - rects[i].width = boxes[i].p2.x - boxes[i].p1.x; - rects[i].height = boxes[i].p2.y - boxes[i].p1.y; + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; } - _cairo_region_boxes_fini (&bounded, boxes); - _cairo_region_fini (&bounded); - _cairo_region_fini (&bound); + if (unlikely (region == &bounded)) + _cairo_region_fini (&bounded); surface->have_clip_rects = TRUE; surface->clip_rects = rects; - surface->num_clip_rects = n_boxes; + surface->num_clip_rects = n_rects; /* Discard the trivial clip rectangle that covers the entire surface */ - if (n_boxes == 1 && + if (n_rects == 1 && rects[0].x == 0 && rects[0].y == 0 && rects[0].width == surface->width && @@ -2348,7 +2451,7 @@ _cairo_xlib_surface_get_font_options (void *abstract_surface, { cairo_xlib_surface_t *surface = abstract_surface; - *options = surface->screen_info->font_options; + *options = *_cairo_xlib_screen_get_font_options (surface->screen_info); } static void @@ -2393,7 +2496,7 @@ _cairo_xlib_surface_reset (void *abstract_surface) cairo_status_t status; status = _cairo_xlib_surface_set_clip_region (surface, NULL); - if (status) + if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; @@ -2411,6 +2514,8 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_composite, _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_xlib_surface_set_clip_region, @@ -2428,11 +2533,14 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { NULL, /* stroke */ NULL, /* fill */ _cairo_xlib_surface_show_glyphs, - NULL, /* snapshot */ + + _cairo_xlib_surface_snapshot, + _cairo_xlib_surface_is_similar, _cairo_xlib_surface_reset, NULL, /* fill_stroke */ - _cairo_xlib_surface_create_solid_pattern_surface + _cairo_xlib_surface_create_solid_pattern_surface, + _cairo_xlib_surface_can_repaint_solid_pattern_surface }; /** @@ -2496,6 +2604,7 @@ _cairo_xlib_surface_create_internal (Display *dpy, cairo_xlib_surface_t *surface; cairo_xlib_display_t *display; cairo_xlib_screen_info_t *screen_info; + cairo_status_t status; CAIRO_MUTEX_INITIALIZE (); @@ -2526,18 +2635,18 @@ _cairo_xlib_surface_create_internal (Display *dpy, if (depth == 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - display = _cairo_xlib_display_get (dpy); - if (display == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + status = _cairo_xlib_display_get (dpy, &display); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); - screen_info = _cairo_xlib_screen_info_get (display, screen); - if (screen_info == NULL) { + status = _cairo_xlib_screen_info_get (display, screen, &screen_info); + if (unlikely (status)) { _cairo_xlib_display_destroy (display); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + return _cairo_surface_create_in_error (status); } surface = malloc (sizeof (cairo_xlib_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { _cairo_xlib_screen_info_destroy (screen_info); _cairo_xlib_display_destroy (display); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -2547,11 +2656,8 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->close_display_hook.func = _cairo_xlib_surface_detach_display; _cairo_xlib_add_close_display_hook (display, &surface->close_display_hook); - if (! XRenderQueryVersion (dpy, &surface->render_major, &surface->render_minor)) { - surface->render_major = -1; - surface->render_minor = -1; - } - + surface->render_major = display->render_major; + surface->render_minor = display->render_minor; if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { if (!xrender_format) { if (visual) { @@ -2592,6 +2698,7 @@ _cairo_xlib_surface_create_internal (Display *dpy, /* so we can use the XTile fallback */ surface->buggy_repeat = TRUE; } + surface->buggy_pad_reflect = screen_info->display->buggy_pad_reflect; surface->dst_picture = None; surface->src_picture = None; @@ -2604,8 +2711,10 @@ _cairo_xlib_surface_create_internal (Display *dpy, surface->xtransform = identity; surface->have_clip_rects = FALSE; + surface->gc_has_clip_rects = FALSE; surface->clip_rects = surface->embedded_clip_rects; surface->num_clip_rects = 0; + surface->clip_dirty = 0; /* * Compute the pixel format masks from either a XrenderFormat or @@ -2867,7 +2976,7 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, surface->display, XRenderFreePicture, surface->dst_picture); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (&surface->base, status); return; } @@ -2880,7 +2989,7 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, surface->display, XRenderFreePicture, surface->src_picture); - if (status) { + if (unlikely (status)) { status = _cairo_surface_set_error (&surface->base, status); return; } @@ -3128,17 +3237,18 @@ _cairo_xlib_surface_font_init (Display *dpy, cairo_scaled_font_t *scaled_font) { cairo_xlib_surface_font_private_t *font_private; + cairo_status_t status; int i; font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); - if (font_private == NULL) + if (unlikely (font_private == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_private->scaled_font = scaled_font; - font_private->display = _cairo_xlib_display_get (dpy); - if (font_private->display == NULL) { + status = _cairo_xlib_display_get (dpy, &font_private->display); + if (unlikely (status)) { free (font_private); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return status; } /* initialize and hook into the CloseDisplay callback */ @@ -3252,7 +3362,7 @@ _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, to_free, free); /* XXX cannot propagate failure */ - if (status) + if (unlikely (status)) free (to_free); to_free = glyphset_info->pending_free_glyphs = NULL; @@ -3260,7 +3370,7 @@ _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, if (to_free == NULL) { to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t)); - if (to_free == NULL) { + if (unlikely (to_free == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return; /* XXX cannot propagate failure */ } @@ -3408,7 +3518,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, pscaled_glyph); - if (status) + if (unlikely (status)) return status; scaled_glyph = *pscaled_glyph; @@ -3420,7 +3530,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, if (scaled_font->surface_private == NULL) { status = _cairo_xlib_surface_font_init (dpy, scaled_font); - if (status) + if (unlikely (status)) return status; } @@ -3447,7 +3557,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); status = tmp_surface->status; - if (status) + if (unlikely (status)) goto BAIL; cr = cairo_create (tmp_surface); @@ -3461,7 +3571,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, glyph_surface = (cairo_image_surface_t *) tmp_surface; - if (status) + if (unlikely (status)) goto BAIL; } @@ -3477,7 +3587,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, glyph_surface->width, glyph_surface->height); status = tmp_surface->status; - if (status) + if (unlikely (status)) goto BAIL; tmp_surface->device_transform = glyph_surface->base.device_transform; @@ -3492,7 +3602,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy, glyph_surface = (cairo_image_surface_t *) tmp_surface; - if (status) + if (unlikely (status)) goto BAIL; } @@ -3522,14 +3632,13 @@ _cairo_xlib_surface_add_glyph (Display *dpy, } n = new; d = data; - while (c--) - { + do { char b = *d++; b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); *n++ = b; - } + } while (--c); data = new; } break; @@ -3537,28 +3646,21 @@ _cairo_xlib_surface_add_glyph (Display *dpy, break; case CAIRO_FORMAT_ARGB32: if (_native_byte_order_lsb() != (ImageByteOrder (dpy) == LSBFirst)) { - unsigned int c = glyph_surface->stride * glyph_surface->height; - unsigned char *d; - unsigned char *new, *n; + unsigned int c = glyph_surface->stride * glyph_surface->height / 4; + const uint32_t *d; + uint32_t *new, *n; - new = malloc (c); - if (new == NULL) { + new = malloc (4 * c); + if (unlikely (new == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } n = new; - d = data; - while (c >= 4) - { - n[3] = d[0]; - n[2] = d[1]; - n[1] = d[2]; - n[0] = d[3]; - d += 4; - n += 4; - c -= 4; - } - data = new; + d = (uint32_t *) data; + do { + *n++ = bswap_32 (*d++); + } while (--c); + data = (uint8_t *) new; } break; case CAIRO_FORMAT_RGB24: @@ -3686,7 +3788,7 @@ _emit_glyphs_chunk (cairo_xlib_surface_t *dst, elts = stack_elts; } else { elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); - if (elts == NULL) + if (unlikely (elts == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -3827,7 +3929,7 @@ _cairo_xlib_surface_emit_glyphs (cairo_xlib_surface_t *dst, status = _cairo_xlib_surface_add_glyph (dst->dpy, scaled_font, &scaled_glyph); - if (status) { + if (unlikely (status)) { if (status == CAIRO_INT_STATUS_UNSUPPORTED) /* Break so we flush glyphs so far and let fallback code * handle the rest */ @@ -3945,11 +4047,12 @@ _cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, static cairo_int_status_t _cairo_xlib_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, - cairo_pattern_t *src_pattern, + const cairo_pattern_t *src_pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs) + int *remaining_glyphs, + cairo_rectangle_int_t *extents) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; @@ -4014,10 +4117,12 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, 0, 0, 1, 1, + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attributes); - if (status) + if (unlikely (status)) goto BAIL0; } else { cairo_rectangle_int_t glyph_extents; @@ -4026,15 +4131,19 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, glyphs, num_glyphs, &glyph_extents); - if (status) + if (unlikely (status)) goto BAIL0; status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, + CAIRO_CONTENT_COLOR_ALPHA, glyph_extents.x, glyph_extents.y, glyph_extents.width, glyph_extents.height, + dst->buggy_pad_reflect ? + CAIRO_PATTERN_ACQUIRE_NO_REFLECT : + CAIRO_PATTERN_ACQUIRE_NONE, (cairo_surface_t **) &src, &attributes); - if (status) + if (unlikely (status)) goto BAIL0; } @@ -4045,8 +4154,8 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, goto BAIL1; } - status = _cairo_xlib_surface_set_attributes (src, &attributes); - if (status) + status = _cairo_xlib_surface_set_attributes (src, &attributes, 0, 0); + if (unlikely (status)) goto BAIL1; _cairo_scaled_font_freeze_cache (scaled_font); diff --git a/src/cairo-xlib-visual.c b/src/cairo-xlib-visual.c index f6eb1ee..7dbe86c 100644 --- a/src/cairo-xlib-visual.c +++ b/src/cairo-xlib-visual.c @@ -78,7 +78,7 @@ _cairo_xlib_visual_info_create (Display *dpy, ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1); info = malloc (sizeof (cairo_xlib_visual_info_t)); - if (info == NULL) + if (unlikely (info == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); info->visualid = visualid; diff --git a/src/cairo.c b/src/cairo.c index a2e9842..91b8828 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -49,9 +49,7 @@ static const cairo_t _cairo_nil = { CAIRO_STATUS_NO_MEMORY, /* status */ { 0, 0, 0, NULL }, /* user_data */ NULL, /* gstate */ - {{ /* gstate_tail */ - 0 - }}, + {{ 0 }, { 0 }}, /* gstate_tail */ NULL, /* gstate_freelist */ {{ /* path */ { 0, 0 }, /* last_move_point */ @@ -84,6 +82,7 @@ static const cairo_t _cairo_nil = { cairo_status_t _cairo_error (cairo_status_t status) { + CAIRO_ENSURE_UNIQUE; assert (_cairo_status_is_error (status)); return status; @@ -118,6 +117,53 @@ _cairo_set_error (cairo_t *cr, cairo_status_t status) status = _cairo_error (status); } +#if HAS_ATOMIC_OPS +/* We keep a small stash of contexts to reduce malloc pressure */ +#define CAIRO_STASH_SIZE 4 +static struct { + cairo_t pool[CAIRO_STASH_SIZE]; + int occupied; +} _context_stash; + +static cairo_t * +_context_get (void) +{ + int avail, old, new; + + do { + old = _context_stash.occupied; + avail = ffs (~old) - 1; + if (avail >= CAIRO_STASH_SIZE) + return malloc (sizeof (cairo_t)); + + new = old | (1 << avail); + } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new) != old); + + return &_context_stash.pool[avail]; +} + +static void +_context_put (cairo_t *cr) +{ + int old, new, avail; + + if (cr < &_context_stash.pool[0] || + cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) + { + return free (cr); + } + + avail = ~(1 << (cr - &_context_stash.pool[0])); + do { + old = _context_stash.occupied; + new = old & avail; + } while (_cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new) != old); +} +#else +#define _context_get() malloc (sizeof (cairo_t)) +#define _context_put(cr) free (cr) +#endif + /** * cairo_create: * @target: target surface for the context @@ -151,8 +197,8 @@ cairo_create (cairo_surface_t *target) if (target && target->status == CAIRO_STATUS_NO_MEMORY) return (cairo_t *) &_cairo_nil; - cr = malloc (sizeof (cairo_t)); - if (cr == NULL) { + cr = _context_get (); + if (unlikely (cr == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_t *) &_cairo_nil; } @@ -164,11 +210,12 @@ cairo_create (cairo_surface_t *target) _cairo_user_data_array_init (&cr->user_data); _cairo_path_fixed_init (cr->path); - cr->gstate = cr->gstate_tail; - cr->gstate_freelist = NULL; - status = _cairo_gstate_init (cr->gstate, target); + cr->gstate = &cr->gstate_tail[0]; + cr->gstate_freelist = &cr->gstate_tail[1]; + cr->gstate_tail[1].next = NULL; - if (status) + status = _cairo_gstate_init (cr->gstate, target); + if (unlikely (status)) _cairo_set_error (cr, status); return cr; @@ -212,6 +259,8 @@ cairo_reference (cairo_t *cr) void cairo_destroy (cairo_t *cr) { + cairo_surface_t *surface; + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) return; @@ -220,12 +269,22 @@ cairo_destroy (cairo_t *cr) if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) return; - while (cr->gstate != cr->gstate_tail) { + while (cr->gstate != &cr->gstate_tail[0]) { if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist)) break; } + /* The context is expected (>99% of all use cases) to be held for the + * duration of a single expose event/sequence of graphic operations. + * Therefore, on destroy we explicitly flush the Cairo pipeline of any + * pending operations. + */ + surface = _cairo_gstate_get_original_target (cr->gstate); + if (surface != NULL) + cairo_surface_flush (surface); + _cairo_gstate_fini (cr->gstate); + cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */ while (cr->gstate_freelist != NULL) { cairo_gstate_t *gstate = cr->gstate_freelist; cr->gstate_freelist = gstate->next; @@ -236,7 +295,7 @@ cairo_destroy (cairo_t *cr) _cairo_user_data_array_fini (&cr->user_data); - free (cr); + _context_put (cr); } slim_hidden_def (cairo_destroy); @@ -338,7 +397,7 @@ cairo_save (cairo_t *cr) return; status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_save); @@ -360,7 +419,7 @@ cairo_restore (cairo_t *cr) return; status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_restore); @@ -414,7 +473,6 @@ cairo_push_group (cairo_t *cr) { cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); } -slim_hidden_def(cairo_push_group); /** * cairo_push_group_with_content: @@ -448,10 +506,10 @@ cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) parent_surface = _cairo_gstate_get_target (cr->gstate); /* Get the extents that we'll use in creating our new group surface */ status = _cairo_surface_get_extents (parent_surface, &extents); - if (status) + if (unlikely (status)) goto bail; status = _cairo_clip_intersect_to_rectangle (_cairo_gstate_get_clip (cr->gstate), &extents); - if (status) + if (unlikely (status)) goto bail; group_surface = cairo_surface_create_similar (_cairo_gstate_get_target (cr->gstate), @@ -459,7 +517,7 @@ cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) extents.width, extents.height); status = cairo_surface_status (group_surface); - if (status) + if (unlikely (status)) goto bail; /* Set device offsets on the new surface so that logically it appears at @@ -480,7 +538,7 @@ cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) bail: cairo_surface_destroy (group_surface); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_push_group_with_content); @@ -597,7 +655,6 @@ cairo_pop_group_to_source (cairo_t *cr) cairo_set_source (cr, group_pattern); cairo_pattern_destroy (group_pattern); } -slim_hidden_def(cairo_pop_group_to_source); /** * cairo_set_operator: @@ -619,11 +676,35 @@ cairo_set_operator (cairo_t *cr, cairo_operator_t op) return; status = _cairo_gstate_set_operator (cr->gstate, op); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_operator); + +static cairo_bool_t +_current_source_matches_solid (cairo_t *cr, + double red, + double green, + double blue, + double alpha) +{ + const cairo_pattern_t *current; + cairo_color_t color; + + current = cr->gstate->source; + if (current->type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + return _cairo_color_equal (&color, + &((cairo_solid_pattern_t *) current)->color); +} /** * cairo_set_source_rgb * @cr: a cairo context @@ -650,13 +731,17 @@ cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) if (cr->status) return; + if (_current_source_matches_solid (cr, red, green, blue, 1.)) + return; + /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_none); + cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); pattern = cairo_pattern_create_rgb (red, green, blue); cairo_set_source (cr, pattern); cairo_pattern_destroy (pattern); } +slim_hidden_def (cairo_set_source_rgb); /** * cairo_set_source_rgba: @@ -687,8 +772,11 @@ cairo_set_source_rgba (cairo_t *cr, if (cr->status) return; + if (_current_source_matches_solid (cr, red, green, blue, alpha)) + return; + /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_none); + cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); pattern = cairo_pattern_create_rgba (red, green, blue, alpha); cairo_set_source (cr, pattern); @@ -731,7 +819,7 @@ cairo_set_source_surface (cairo_t *cr, return; /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_none); + cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); pattern = cairo_pattern_create_for_surface (surface); @@ -781,7 +869,7 @@ cairo_set_source (cairo_t *cr, cairo_pattern_t *source) } status = _cairo_gstate_set_source (cr->gstate, source); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_source); @@ -829,12 +917,14 @@ cairo_set_tolerance (cairo_t *cr, double tolerance) if (cr->status) return; - _cairo_restrict_value (&tolerance, CAIRO_TOLERANCE_MINIMUM, tolerance); + if (tolerance < CAIRO_TOLERANCE_MINIMUM) + tolerance = CAIRO_TOLERANCE_MINIMUM; status = _cairo_gstate_set_tolerance (cr->gstate, tolerance); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_set_tolerance); /** * cairo_set_antialias: @@ -858,7 +948,7 @@ cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) return; status = _cairo_gstate_set_antialias (cr->gstate, antialias); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -884,7 +974,7 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) return; status = _cairo_gstate_set_fill_rule (cr->gstate, fill_rule); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -922,10 +1012,11 @@ cairo_set_line_width (cairo_t *cr, double width) if (cr->status) return; - _cairo_restrict_value (&width, 0.0, width); + if (width < 0.) + width = 0.; status = _cairo_gstate_set_line_width (cr->gstate, width); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_line_width); @@ -955,7 +1046,7 @@ cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) return; status = _cairo_gstate_set_line_cap (cr->gstate, line_cap); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_line_cap); @@ -985,7 +1076,7 @@ cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) return; status = _cairo_gstate_set_line_join (cr->gstate, line_join); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_line_join); @@ -1035,7 +1126,7 @@ cairo_set_dash (cairo_t *cr, status = _cairo_gstate_set_dash (cr->gstate, dashes, num_dashes, offset); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1125,7 +1216,7 @@ cairo_set_miter_limit (cairo_t *cr, double limit) return; status = _cairo_gstate_set_miter_limit (cr->gstate, limit); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1150,9 +1241,10 @@ cairo_translate (cairo_t *cr, double tx, double ty) return; status = _cairo_gstate_translate (cr->gstate, tx, ty); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_translate); /** * cairo_scale: @@ -1174,7 +1266,7 @@ cairo_scale (cairo_t *cr, double sx, double sy) return; status = _cairo_gstate_scale (cr->gstate, sx, sy); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_scale); @@ -1200,7 +1292,7 @@ cairo_rotate (cairo_t *cr, double angle) return; status = _cairo_gstate_rotate (cr->gstate, angle); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1223,9 +1315,10 @@ cairo_transform (cairo_t *cr, return; status = _cairo_gstate_transform (cr->gstate, matrix); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_transform); /** * cairo_set_matrix: @@ -1245,7 +1338,7 @@ cairo_set_matrix (cairo_t *cr, return; status = _cairo_gstate_set_matrix (cr->gstate, matrix); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_matrix); @@ -1286,6 +1379,7 @@ cairo_user_to_device (cairo_t *cr, double *x, double *y) _cairo_gstate_user_to_device (cr->gstate, x, y); } +slim_hidden_def (cairo_user_to_device); /** * cairo_user_to_device_distance: @@ -1306,6 +1400,7 @@ cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); } +slim_hidden_def (cairo_user_to_device_distance); /** * cairo_device_to_user: @@ -1360,6 +1455,7 @@ cairo_new_path (cairo_t *cr) return; _cairo_path_fixed_fini (cr->path); + _cairo_path_fixed_init (cr->path); } slim_hidden_def(cairo_new_path); @@ -1386,7 +1482,7 @@ cairo_move_to (cairo_t *cr, double x, double y) y_fixed = _cairo_fixed_from_double (y); status = _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_move_to); @@ -1445,7 +1541,7 @@ cairo_line_to (cairo_t *cr, double x, double y) y_fixed = _cairo_fixed_from_double (y); status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_line_to); @@ -1500,7 +1596,7 @@ cairo_curve_to (cairo_t *cr, x1_fixed, y1_fixed, x2_fixed, y2_fixed, x3_fixed, y3_fixed); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_curve_to); @@ -1634,7 +1730,7 @@ cairo_arc_to (cairo_t *cr, x1, y1, x2, y2, radius); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } */ @@ -1670,7 +1766,7 @@ cairo_rel_move_to (cairo_t *cr, double dx, double dy) dy_fixed = _cairo_fixed_from_double (dy); status = _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1707,7 +1803,7 @@ cairo_rel_line_to (cairo_t *cr, double dx, double dy) dy_fixed = _cairo_fixed_from_double (dy); status = _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_rel_line_to); @@ -1768,7 +1864,7 @@ cairo_rel_curve_to (cairo_t *cr, dx1_fixed, dy1_fixed, dx2_fixed, dy2_fixed, dx3_fixed, dy3_fixed); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1807,12 +1903,6 @@ cairo_rectangle (cairo_t *cr, cairo_close_path (cr); } -/** - * cairo_stroke_to_path: - * @cr: a cairo context - * - * This function is not yet implemented. - **/ #if 0 /* XXX: NYI */ void @@ -1826,7 +1916,7 @@ cairo_stroke_to_path (cairo_t *cr) /* The code in _cairo_meta_surface_get_path has a poorman's stroke_to_path */ status = _cairo_gstate_stroke_path (cr->gstate); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } #endif @@ -1866,7 +1956,7 @@ cairo_close_path (cairo_t *cr) return; status = _cairo_path_fixed_close_path (cr->path); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_close_path); @@ -1906,8 +1996,6 @@ void cairo_path_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { - cairo_status_t status; - if (cr->status) { if (x1) *x1 = 0.0; @@ -1921,13 +2009,10 @@ cairo_path_extents (cairo_t *cr, return; } - status = _cairo_gstate_path_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); - if (status) - _cairo_set_error (cr, status); + _cairo_gstate_path_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); } -slim_hidden_def (cairo_path_extents); /** * cairo_paint: @@ -1945,7 +2030,7 @@ cairo_paint (cairo_t *cr) return; status = _cairo_gstate_paint (cr->gstate); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_paint); @@ -1984,7 +2069,7 @@ cairo_paint_with_alpha (cairo_t *cr, _cairo_pattern_init_solid (&pattern, &color, CAIRO_CONTENT_ALPHA); status = _cairo_gstate_mask (cr->gstate, &pattern.base); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); _cairo_pattern_fini (&pattern.base); @@ -2020,7 +2105,7 @@ cairo_mask (cairo_t *cr, } status = _cairo_gstate_mask (cr->gstate, pattern); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_mask); @@ -2122,7 +2207,7 @@ cairo_stroke_preserve (cairo_t *cr) return; status = _cairo_gstate_stroke (cr->gstate, cr->path); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_stroke_preserve); @@ -2165,7 +2250,7 @@ cairo_fill_preserve (cairo_t *cr) return; status = _cairo_gstate_fill (cr->gstate, cr->path); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_fill_preserve); @@ -2191,7 +2276,7 @@ cairo_copy_page (cairo_t *cr) return; status = _cairo_gstate_copy_page (cr->gstate); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2214,7 +2299,7 @@ cairo_show_page (cairo_t *cr) return; status = _cairo_gstate_show_page (cr->gstate); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2248,7 +2333,7 @@ cairo_in_stroke (cairo_t *cr, double x, double y) status = _cairo_gstate_in_stroke (cr->gstate, cr->path, x, y, &inside); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); return inside; @@ -2273,17 +2358,14 @@ cairo_in_stroke (cairo_t *cr, double x, double y) cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y) { - cairo_status_t status; - cairo_bool_t inside = FALSE; + cairo_bool_t inside; if (cr->status) return 0; - status = _cairo_gstate_in_fill (cr->gstate, - cr->path, - x, y, &inside); - if (status) - _cairo_set_error (cr, status); + _cairo_gstate_in_fill (cr->gstate, + cr->path, + x, y, &inside); return inside; } @@ -2338,7 +2420,7 @@ cairo_stroke_extents (cairo_t *cr, status = _cairo_gstate_stroke_extents (cr->gstate, cr->path, x1, y1, x2, y2); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2389,7 +2471,7 @@ cairo_fill_extents (cairo_t *cr, status = _cairo_gstate_fill_extents (cr->gstate, cr->path, x1, y1, x2, y2); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2454,7 +2536,7 @@ cairo_clip_preserve (cairo_t *cr) return; status = _cairo_gstate_clip (cr->gstate, cr->path); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_clip_preserve); @@ -2484,7 +2566,7 @@ cairo_reset_clip (cairo_t *cr) return; status = _cairo_gstate_reset_clip (cr->gstate); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2522,7 +2604,7 @@ cairo_clip_extents (cairo_t *cr, } status = _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2535,7 +2617,7 @@ _cairo_rectangle_list_create_in_error (cairo_status_t status) return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; list = malloc (sizeof (cairo_rectangle_list_t)); - if (list == NULL) { + if (unlikely (list == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; } @@ -2591,6 +2673,13 @@ cairo_copy_clip_rectangle_list (cairo_t *cr) * "sans-serif", "cursive", "fantasy", "monospace"), are likely to * work as expected. * + * If @family starts with the string "@cairo:", or if no native font + * backends are compiled in, cairo will use an internal font family. + * The internal font family recognizes many modifiers in the @family + * string, most notably, it recognizes the string "monospace". That is, + * the family name "@cairo:monospace" will use the monospace version of + * the internal font family. + * * For "real" font selection, see the font-backend-specific * font_face_create functions for the font backend you are using. (For * example, if you are using the freetype-based cairo-ft font backend, @@ -2628,7 +2717,7 @@ cairo_select_font_face (cairo_t *cr, return; status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2656,7 +2745,7 @@ cairo_font_extents (cairo_t *cr, return; status = _cairo_gstate_get_font_extents (cr->gstate, extents); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2679,7 +2768,7 @@ cairo_set_font_face (cairo_t *cr, return; status = _cairo_gstate_set_font_face (cr->gstate, font_face); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2711,7 +2800,7 @@ cairo_get_font_face (cairo_t *cr) return (cairo_font_face_t*) &_cairo_font_face_nil; status = _cairo_gstate_get_font_face (cr->gstate, &font_face); - if (status) { + if (unlikely (status)) { _cairo_set_error (cr, status); return (cairo_font_face_t*) &_cairo_font_face_nil; } @@ -2743,7 +2832,7 @@ cairo_set_font_size (cairo_t *cr, double size) return; status = _cairo_gstate_set_font_size (cr->gstate, size); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_font_size); @@ -2771,7 +2860,7 @@ cairo_set_font_matrix (cairo_t *cr, return; status = _cairo_gstate_set_font_matrix (cr->gstate, matrix); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2815,7 +2904,7 @@ cairo_set_font_options (cairo_t *cr, return; status = cairo_font_options_status ((cairo_font_options_t *) options); - if (status) { + if (unlikely (status)) { _cairo_set_error (cr, status); return; } @@ -2869,29 +2958,38 @@ cairo_set_scaled_font (cairo_t *cr, const cairo_scaled_font_t *scaled_font) { cairo_status_t status; + cairo_bool_t was_previous; if (cr->status) return; if (scaled_font == NULL) { - status = CAIRO_STATUS_NULL_POINTER; + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto BAIL; } status = scaled_font->status; - if (status) + if (unlikely (status)) goto BAIL; + if (scaled_font == cr->gstate->scaled_font) + return; + + was_previous = scaled_font == cr->gstate->previous_scaled_font; + status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); - if (status) + if (unlikely (status)) goto BAIL; status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); - if (status) + if (unlikely (status)) goto BAIL; _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); + if (was_previous) + cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font); + return; BAIL: @@ -2928,7 +3026,7 @@ cairo_get_scaled_font (cairo_t *cr) return _cairo_scaled_font_create_in_error (cr->status); status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font); - if (status) { + if (unlikely (status)) { _cairo_set_error (cr, status); return _cairo_scaled_font_create_in_error (status); } @@ -2994,7 +3092,7 @@ cairo_text_extents (cairo_t *cr, extents); cairo_glyph_free (glyphs); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3049,7 +3147,7 @@ cairo_glyph_extents (cairo_t *cr, status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3085,12 +3183,14 @@ cairo_show_text (cairo_t *cr, const char *utf8) { cairo_text_extents_t extents; cairo_status_t status; - cairo_glyph_t *glyphs = NULL, *last_glyph; - cairo_text_cluster_t *clusters = NULL; + cairo_glyph_t *glyphs, *last_glyph; + cairo_text_cluster_t *clusters; int utf8_len, num_glyphs, num_clusters; cairo_text_cluster_flags_t cluster_flags; double x, y; cairo_bool_t has_show_text_glyphs; + cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; if (cr->status) return; @@ -3105,13 +3205,24 @@ cairo_show_text (cairo_t *cr, const char *utf8) has_show_text_glyphs = cairo_surface_has_show_text_glyphs (cairo_get_target (cr)); + glyphs = stack_glyphs; + num_glyphs = ARRAY_LENGTH (stack_glyphs); + + if (has_show_text_glyphs) { + clusters = stack_clusters; + num_clusters = ARRAY_LENGTH (stack_clusters); + } else { + clusters = NULL; + num_clusters = 0; + } + status = _cairo_gstate_text_to_glyphs (cr->gstate, x, y, utf8, utf8_len, &glyphs, &num_glyphs, has_show_text_glyphs ? &clusters : NULL, &num_clusters, &cluster_flags); - if (status) + if (unlikely (status)) goto BAIL; if (num_glyphs == 0) @@ -3122,14 +3233,14 @@ cairo_show_text (cairo_t *cr, const char *utf8) glyphs, num_glyphs, clusters, num_clusters, cluster_flags); - if (status) + if (unlikely (status)) goto BAIL; last_glyph = &glyphs[num_glyphs - 1]; status = _cairo_gstate_glyph_extents (cr->gstate, last_glyph, 1, &extents); - if (status) + if (unlikely (status)) goto BAIL; x = last_glyph->x + extents.x_advance; @@ -3137,10 +3248,12 @@ cairo_show_text (cairo_t *cr, const char *utf8) cairo_move_to (cr, x, y); BAIL: - cairo_glyph_free (glyphs); - cairo_text_cluster_free (clusters); + if (glyphs != stack_glyphs) + cairo_glyph_free (glyphs); + if (clusters != stack_clusters) + cairo_text_cluster_free (clusters); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3180,7 +3293,7 @@ cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) glyphs, num_glyphs, NULL, 0, FALSE); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3283,7 +3396,7 @@ cairo_show_text_glyphs (cairo_t *cr, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3316,7 +3429,8 @@ cairo_text_path (cairo_t *cr, const char *utf8) { cairo_status_t status; cairo_text_extents_t extents; - cairo_glyph_t *glyphs = NULL, *last_glyph; + cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_glyph_t *glyphs, *last_glyph; int num_glyphs; double x, y; @@ -3328,6 +3442,9 @@ cairo_text_path (cairo_t *cr, const char *utf8) cairo_get_current_point (cr, &x, &y); + glyphs = stack_glyphs; + num_glyphs = ARRAY_LENGTH (stack_glyphs); + status = _cairo_gstate_text_to_glyphs (cr->gstate, x, y, utf8, strlen (utf8), @@ -3335,7 +3452,7 @@ cairo_text_path (cairo_t *cr, const char *utf8) NULL, NULL, NULL); - if (status) + if (unlikely (status)) goto BAIL; if (num_glyphs == 0) @@ -3345,7 +3462,7 @@ cairo_text_path (cairo_t *cr, const char *utf8) glyphs, num_glyphs, cr->path); - if (status) + if (unlikely (status)) goto BAIL; last_glyph = &glyphs[num_glyphs - 1]; @@ -3353,7 +3470,7 @@ cairo_text_path (cairo_t *cr, const char *utf8) last_glyph, 1, &extents); - if (status) + if (unlikely (status)) goto BAIL; x = last_glyph->x + extents.x_advance; @@ -3361,9 +3478,10 @@ cairo_text_path (cairo_t *cr, const char *utf8) cairo_move_to (cr, x, y); BAIL: - cairo_glyph_free (glyphs); + if (glyphs != stack_glyphs) + cairo_glyph_free (glyphs); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3401,7 +3519,7 @@ cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) status = _cairo_gstate_glyph_path (cr->gstate, glyphs, num_glyphs, cr->path); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3444,7 +3562,7 @@ slim_hidden_def (cairo_get_tolerance); * cairo_get_antialias: * @cr: a cairo context * - * Gets the current shape antialiasing mode, as set by cairo_set_antialias(). + * Gets the current shape antialiasing mode, as set by cairo_set_shape_antialias(). * * Return value: the current shape antialiasing mode. **/ @@ -3817,7 +3935,7 @@ cairo_append_path (cairo_t *cr, } status = _cairo_path_append_to_context (path, cr); - if (status) + if (unlikely (status)) _cairo_set_error (cr, status); } diff --git a/src/cairo.h b/src/cairo.h index cf4bc05..2fef37c 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -210,7 +210,7 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_SUCCESS: no error has occurred * @CAIRO_STATUS_NO_MEMORY: out of memory * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() - * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop + * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t @@ -239,6 +239,12 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8) * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8) * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8) + * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10) + * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10) + * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of + * status values defined in this enumeration. When using this value, note + * that the version of cairo at run-time may have additional status values + * defined than the value of this symbol at compile-time. (Since 1.10) * * #cairo_status_t is used to indicate errors that can occur when * using Cairo. In some cases it is returned directly by functions. @@ -250,6 +256,7 @@ typedef struct _cairo_user_data_key { **/ typedef enum _cairo_status { CAIRO_STATUS_SUCCESS = 0, + CAIRO_STATUS_NO_MEMORY, CAIRO_STATUS_INVALID_RESTORE, CAIRO_STATUS_INVALID_POP_GROUP, @@ -280,8 +287,11 @@ typedef enum _cairo_status { CAIRO_STATUS_NEGATIVE_COUNT, CAIRO_STATUS_INVALID_CLUSTERS, CAIRO_STATUS_INVALID_SLANT, - CAIRO_STATUS_INVALID_WEIGHT - /* after adding a new error: update CAIRO_STATUS_LAST_STATUS in cairoint.h */ + CAIRO_STATUS_INVALID_WEIGHT, + CAIRO_STATUS_INVALID_SIZE, + CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, + + CAIRO_STATUS_LAST_STATUS } cairo_status_t; /** @@ -1445,8 +1455,7 @@ cairo_user_font_face_create (void); * point and trying to use it for text operations in the callback will result * in deadlock. * - * Returns: %CAIRO_STATUS_SUCCESS upon success, or - * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * Returns: %CAIRO_STATUS_SUCCESS upon success, or an error status on error. * * Since: 1.8 **/ @@ -1547,7 +1556,8 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal * will free the allocated cluster array using cairo_text_cluster_free(). * * The callback is optional. If @num_glyphs is negative upon - * the callback returning, the unicode_to_glyph callback + * the callback returning or if the return value + * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback * is tried. See #cairo_user_scaled_font_unicode_to_glyph_func_t. * * Note: While cairo does not impose any limitation on glyph indices, @@ -1558,8 +1568,9 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal * are advised to use glyph 0 for such purposes and do not use that * glyph value for other purposes. * - * Returns: %CAIRO_STATUS_SUCCESS upon success, or - * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. * * Since: 1.8 **/ @@ -1592,8 +1603,9 @@ typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_sc * complex scripts can be implemented using this callback. * * The callback is optional, and only used if text_to_glyphs callback is not - * set or fails to return glyphs. If this callback is not set, an identity - * mapping from Unicode code-points to glyph indices is assumed. + * set or fails to return glyphs. If this callback is not set or if it returns + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode + * code-points to glyph indices is assumed. * * Note: While cairo does not impose any limitation on glyph indices, * some applications may assume that a glyph index fits in a 16-bit @@ -1603,8 +1615,9 @@ typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_sc * are advised to use glyph 0 for such purposes and do not use that * glyph value for other purposes. * - * Returns: %CAIRO_STATUS_SUCCESS upon success, or - * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. * * Since: 1.8 **/ @@ -1875,6 +1888,7 @@ cairo_surface_status (cairo_surface_t *surface); * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2 * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image + * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 * * #cairo_surface_type_t is used to describe the type of a given * surface. The surface types are also known as "backends" or "surface @@ -1913,7 +1927,8 @@ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_SVG, CAIRO_SURFACE_TYPE_OS2, CAIRO_SURFACE_TYPE_WIN32_PRINTING, - CAIRO_SURFACE_TYPE_QUARTZ_IMAGE + CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, + CAIRO_SURFACE_TYPE_SCRIPT } cairo_surface_type_t; cairo_public cairo_surface_type_t @@ -1945,6 +1960,24 @@ cairo_surface_set_user_data (cairo_surface_t *surface, void *user_data, cairo_destroy_func_t destroy); +#define CAIRO_MIME_TYPE_JPEG "image/jpeg" +#define CAIRO_MIME_TYPE_PNG "image/png" +#define CAIRO_MIME_TYPE_JP2 "image/jp2" + +cairo_public void +cairo_surface_get_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char **data, + unsigned int *length); + +cairo_public cairo_status_t +cairo_surface_set_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char *data, + unsigned int length, + cairo_destroy_func_t destroy, + void *closure); + cairo_public void cairo_surface_get_font_options (cairo_surface_t *surface, cairo_font_options_t *options); @@ -2319,10 +2352,88 @@ cairo_public void cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y); +/* Region functions */ + +typedef struct _cairo_region cairo_region_t; + +typedef struct _cairo_rectangle_int { + int x, y; + int width, height; +} cairo_rectangle_int_t; + +typedef enum _cairo_region_overlap { + CAIRO_REGION_OVERLAP_IN, /* completely inside region */ + CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ + CAIRO_REGION_OVERLAP_PART /* partly inside region */ +} cairo_region_overlap_t; + +cairo_public cairo_region_t * +cairo_region_create (void); + +cairo_public cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_region_t * +cairo_region_copy (cairo_region_t *original); + +cairo_public void +cairo_region_destroy (cairo_region_t *region); + +cairo_public cairo_status_t +cairo_region_status (cairo_region_t *region); + +cairo_public void +cairo_region_get_extents (cairo_region_t *region, + cairo_rectangle_int_t *extents); + +cairo_public int +cairo_region_num_rectangles (cairo_region_t *region); + +cairo_public void +cairo_region_get_rectangle (cairo_region_t *region, + int nth_rectangle, + cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_is_empty (cairo_region_t *region); + +cairo_public cairo_region_overlap_t +cairo_region_contains_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_contains_point (cairo_region_t *region, int x, int y); + +cairo_public void +cairo_region_translate (cairo_region_t *region, int dx, int dy); + +cairo_public cairo_status_t +cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_intersect (cairo_region_t *dst, cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_union (cairo_region_t *dst, cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + + /* Functions to be used while debugging (not intended for use in production code) */ cairo_public void cairo_debug_reset_static_data (void); + CAIRO_END_DECLS #endif /* CAIRO_H */ diff --git a/src/cairoint.h b/src/cairoint.h index 43c5ae8..9021ebf 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -79,13 +79,13 @@ #define CAIRO_HAS_FONT_SUBSET 1 #endif -#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE +#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_FONT_SUBSET #define CAIRO_HAS_PDF_OPERATORS 1 #endif CAIRO_BEGIN_DECLS -#ifdef _WIN32 +#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */ cairo_private FILE * _cairo_win32_tmpfile (void); #define tmpfile() _cairo_win32_tmpfile() @@ -120,16 +120,14 @@ _cairo_win32_tmpfile (void); #undef ARRAY_LENGTH #define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0]))) - -/* This has to be updated whenever #cairo_status_t is extended. That's - * a bit of a pain, but it should be easy to always catch as long as - * one adds a new test case to test a trigger of the new status value. - */ -#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_INVALID_WEIGHT +#undef STRINGIFY +#undef STRINGIFY_ARG +#define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string) +#define STRINGIFY_ARG(contents) #contents #ifdef __GNUC__ #define cairo_container_of(ptr, type, member) ({ \ - const typeof(((type *) 0)->member) *mptr__ = (ptr); \ + const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \ (type *) ((char *) mptr__ - offsetof (type, member)); \ }) #else @@ -153,7 +151,7 @@ do { \ assert (NOT_REACHED); \ } while (0) #define COMPILE_TIME_ASSERT1(condition, line) \ - typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1]; + typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1] #define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line) #define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__) @@ -176,7 +174,7 @@ do { \ * in libgcc in case a target does not have one, which should be just as * good as the open-coded solution below, (which is "HACKMEM 169"). */ -static inline int +static inline int cairo_const _cairo_popcount (uint32_t mask) { #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) @@ -205,25 +203,25 @@ _cairo_popcount (uint32_t mask) #else -static inline uint16_t +static inline uint16_t cairo_const cpu_to_be16(uint16_t v) { return (v << 8) | (v >> 8); } -static inline uint16_t +static inline uint16_t cairo_const be16_to_cpu(uint16_t v) { return cpu_to_be16 (v); } -static inline uint32_t +static inline uint32_t cairo_const cpu_to_be32(uint32_t v) { return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); } -static inline uint32_t +static inline uint32_t cairo_const be32_to_cpu(uint32_t v) { return cpu_to_be32 (v); @@ -234,6 +232,7 @@ be32_to_cpu(uint32_t v) #include "cairo-types-private.h" #include "cairo-cache-private.h" #include "cairo-reference-count-private.h" +#include "cairo-spans-private.h" cairo_private void _cairo_box_from_doubles (cairo_box_t *box, @@ -258,10 +257,19 @@ _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src); cairo_private cairo_bool_t -_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line); +_cairo_box_intersects_line_segment (cairo_box_t *box, + cairo_line_t *line) cairo_pure; cairo_private cairo_bool_t -_cairo_box_contains_point (cairo_box_t *box, cairo_point_t *point); +_cairo_box_contains_point (cairo_box_t *box, + const cairo_point_t *point) cairo_pure; + +cairo_private void +_cairo_composite_rectangles_init (cairo_composite_rectangles_t *rects, + int all_x, + int all_y, + int width, + int height); /* cairo-array.c structures and functions */ @@ -306,6 +314,12 @@ _cairo_array_num_elements (cairo_array_t *array); cairo_private int _cairo_array_size (cairo_array_t *array); +typedef struct { + const cairo_user_data_key_t *key; + void *user_data; + cairo_destroy_func_t destroy; +} cairo_user_data_slot_t; + cairo_private void _cairo_user_data_array_init (cairo_user_data_array_t *array); @@ -322,9 +336,27 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, void *user_data, cairo_destroy_func_t destroy); +cairo_private cairo_status_t +_cairo_user_data_array_copy (cairo_user_data_array_t *dst, + cairo_user_data_array_t *src); + +cairo_private void +_cairo_user_data_array_foreach (cairo_user_data_array_t *array, + void (*func) (const void *key, + void *elt, + void *closure), + void *closure); + +#define _CAIRO_HASH_INIT_VALUE 5381 + cairo_private unsigned long _cairo_hash_string (const char *c); +cairo_private unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *bytes, + unsigned int length); + /* * A #cairo_unscaled_font_t is just an opaque handle we use in the * glyph cache. @@ -336,20 +368,23 @@ typedef struct _cairo_unscaled_font { } cairo_unscaled_font_t; typedef struct _cairo_scaled_glyph { - cairo_cache_entry_t cache_entry; /* hash is glyph index */ - cairo_scaled_font_t *scaled_font; /* font the glyph lives in */ + cairo_hash_entry_t hash_entry; + cairo_text_extents_t metrics; /* user-space metrics */ + cairo_text_extents_t fs_metrics; /* font-space metrics */ cairo_box_t bbox; /* device-space bounds */ int16_t x_advance; /* device-space rounded X advance */ int16_t y_advance; /* device-space rounded Y advance */ + cairo_image_surface_t *surface; /* device-space image */ cairo_path_fixed_t *path; /* device-space outline */ cairo_surface_t *meta_surface; /* device-space meta-surface */ + void *surface_private; /* for the surface backend */ } cairo_scaled_glyph_t; -#define _cairo_scaled_glyph_index(g) ((g)->cache_entry.hash) -#define _cairo_scaled_glyph_set_index(g,i) ((g)->cache_entry.hash = (i)) +#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash) +#define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) #include "cairo-scaled-font-private.h" @@ -363,7 +398,7 @@ struct _cairo_font_face { }; cairo_private void -_cairo_font_face_reset_static_data (void); +_cairo_toy_font_face_reset_static_data (void); cairo_private void _cairo_ft_font_reset_static_data (void); @@ -384,6 +419,8 @@ typedef struct _cairo_toy_font_face { cairo_bool_t owns_family; cairo_font_slant_t slant; cairo_font_weight_t weight; + + cairo_font_face_t *impl_face; /* The non-toy font face this actually uses */ } cairo_toy_font_face_t; typedef enum _cairo_scaled_glyph_info { @@ -412,17 +449,6 @@ typedef struct _cairo_scaled_font_subset { struct _cairo_scaled_font_backend { cairo_font_type_t type; - cairo_warn cairo_status_t - (*get_implementation) (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face); - - cairo_warn cairo_status_t - (*create_toy) (cairo_toy_font_face_t *toy_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **scaled_font); - void (*fini) (void *scaled_font); @@ -453,7 +479,7 @@ struct _cairo_scaled_font_backend { cairo_warn cairo_int_status_t (*show_glyphs) (void *scaled_font, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, @@ -472,7 +498,8 @@ struct _cairo_scaled_font_backend { unsigned char *buffer, unsigned long *length); - /* returns -1 if the unicode character could not be found for the glyph */ + /* ucs4 is set to -1 if the unicode character could not be found + * for the glyph */ cairo_warn cairo_int_status_t (*index_to_ucs4)(void *scaled_font, unsigned long index, @@ -482,6 +509,10 @@ struct _cairo_scaled_font_backend { struct _cairo_font_face_backend { cairo_font_type_t type; + cairo_warn cairo_status_t + (*create_for_toy) (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); + /* The destroy() function is allowed to resurrect the font face * by re-referencing. This is needed for the FreeType backend. */ @@ -489,35 +520,37 @@ struct _cairo_font_face_backend { (*destroy) (void *font_face); cairo_warn cairo_status_t - (*get_implementation) (void *font_face, - cairo_font_face_t **font_face_out); - - cairo_warn cairo_status_t (*scaled_font_create) (void *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **scaled_font); + + cairo_font_face_t * + (*get_implementation) (void *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); }; -extern const cairo_private struct _cairo_scaled_font_backend _cairo_user_scaled_font_backend; +extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend; /* concrete font backends */ #if CAIRO_HAS_FT_FONT -extern const cairo_private struct _cairo_scaled_font_backend _cairo_ft_scaled_font_backend; +extern const cairo_private struct _cairo_font_face_backend _cairo_ft_font_face_backend; #endif #if CAIRO_HAS_WIN32_FONT -extern const cairo_private struct _cairo_scaled_font_backend _cairo_win32_scaled_font_backend; +extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_face_backend; #endif #if CAIRO_HAS_QUARTZ_FONT -extern const cairo_private struct _cairo_scaled_font_backend _cairo_quartz_scaled_font_backend; +extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; #endif @@ -573,6 +606,7 @@ struct _cairo_surface_backend { cairo_warn cairo_status_t (*clone_similar) (void *surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -584,8 +618,8 @@ struct _cairo_surface_backend { /* XXX: dst should be the first argument for consistency */ cairo_warn cairo_int_status_t (*composite) (cairo_operator_t op, - cairo_pattern_t *src, - cairo_pattern_t *mask, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, void *dst, int src_x, int src_y, @@ -606,7 +640,7 @@ struct _cairo_surface_backend { /* XXX: dst should be the first argument for consistency */ cairo_warn cairo_int_status_t (*composite_trapezoids) (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *dst, cairo_antialias_t antialias, int src_x, @@ -618,6 +652,20 @@ struct _cairo_surface_backend { cairo_trapezoid_t *traps, int num_traps); + cairo_warn cairo_span_renderer_t * + (*create_span_renderer) (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects); + + cairo_warn cairo_bool_t + (*check_span_renderer) (cairo_operator_t op, + const cairo_pattern_t *pattern, + void *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects); + cairo_warn cairo_int_status_t (*copy_page) (void *surface); @@ -688,7 +736,7 @@ struct _cairo_surface_backend { cairo_warn cairo_int_status_t (*old_show_glyphs) (cairo_scaled_font_t *font, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, void *surface, int source_x, int source_y, @@ -726,42 +774,47 @@ struct _cairo_surface_backend { cairo_warn cairo_int_status_t (*paint) (void *surface, cairo_operator_t op, - cairo_pattern_t *source); + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents); cairo_warn cairo_int_status_t (*mask) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask); + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents); cairo_warn cairo_int_status_t (*stroke) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents); cairo_warn cairo_int_status_t (*fill) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias); + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents); cairo_warn cairo_int_status_t (*show_glyphs) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - int *remaining_glyphs); + int *remaining_glyphs, + cairo_rectangle_int_t *extents); cairo_surface_t * (*snapshot) (void *surface); @@ -777,23 +830,29 @@ struct _cairo_surface_backend { cairo_warn cairo_int_status_t (*fill_stroke) (void *surface, cairo_operator_t fill_op, - cairo_pattern_t *fill_source, + const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, - cairo_pattern_t *stroke_source, + const cairo_pattern_t *stroke_source, cairo_stroke_style_t *stroke_style, cairo_matrix_t *stroke_ctm, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, - cairo_antialias_t stroke_antialias); + cairo_antialias_t stroke_antialias, + cairo_rectangle_int_t *extents); cairo_surface_t * (*create_solid_pattern_surface) - (void *surface, - cairo_solid_pattern_t *solid_pattern); + (void *surface, + const cairo_solid_pattern_t *solid_pattern); + + cairo_bool_t + (*can_repaint_solid_pattern_surface) + (void *surface, + const cairo_solid_pattern_t *solid_pattern); cairo_bool_t (*has_show_text_glyphs) (void *surface); @@ -801,7 +860,7 @@ struct _cairo_surface_backend { cairo_warn cairo_int_status_t (*show_text_glyphs) (void *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -809,7 +868,8 @@ struct _cairo_surface_backend { const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font); + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents); }; #include "cairo-surface-private.h" @@ -820,9 +880,6 @@ struct _cairo_image_surface { pixman_format_code_t pixman_format; cairo_format_t format; unsigned char *data; - cairo_bool_t owns_data; - cairo_bool_t has_clip; - cairo_image_transparency_t transparency; int width; int height; @@ -830,6 +887,10 @@ struct _cairo_image_surface { int depth; pixman_image_t *pixman_image; + + unsigned owns_data : 1; + unsigned has_clip : 1; + unsigned transparency : 2; }; extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend; @@ -856,7 +917,7 @@ struct _cairo_solid_pattern { }; extern const cairo_private cairo_solid_pattern_t _cairo_pattern_nil; -extern const cairo_private cairo_solid_pattern_t _cairo_pattern_none; +extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black; typedef struct _cairo_surface_pattern { cairo_pattern_t base; @@ -915,7 +976,6 @@ typedef struct _cairo_surface_attributes { cairo_filter_t filter; int x_offset; int y_offset; - cairo_bool_t acquired; void *extra; } cairo_surface_attributes_t; @@ -927,7 +987,8 @@ typedef struct _cairo_traps { int num_traps; int traps_size; cairo_trapezoid_t *traps; - cairo_trapezoid_t traps_embedded[1]; + /* embed enough storage for a stroked rectangle */ + cairo_trapezoid_t traps_embedded[4]; cairo_bool_t has_limits; cairo_box_t limits; @@ -939,26 +1000,27 @@ typedef struct _cairo_traps { #define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial" #define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica" #define CAIRO_FT_FONT_FAMILY_DEFAULT "" +#define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" #if CAIRO_HAS_WIN32_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT -#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &_cairo_win32_scaled_font_backend +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend #elif CAIRO_HAS_QUARTZ_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT -#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &_cairo_quartz_scaled_font_backend +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend #elif CAIRO_HAS_FT_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT -#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &_cairo_ft_scaled_font_backend +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend #else #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT -#define CAIRO_SCALED_FONT_BACKEND_DEFAULT &_cairo_user_scaled_font_backend +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend #endif @@ -983,11 +1045,29 @@ typedef struct _cairo_stroke_face { } cairo_stroke_face_t; /* cairo.c */ -cairo_private void -_cairo_restrict_value (double *value, double min, double max); + +static inline double cairo_const +_cairo_restrict_value (double value, double min, double max) +{ + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +/* C99 round() rounds to the nearest integral value with halfway cases rounded + * away from 0. _cairo_round rounds halfway cases toward negative infinity. + * This matches the rounding behaviour of _cairo_lround. */ +static inline double cairo_const +_cairo_round (double r) +{ + return floor (r + .5); +} cairo_private int -_cairo_lround (double d); +_cairo_lround (double d) cairo_const; /* cairo-gstate.c */ cairo_private cairo_status_t @@ -1122,7 +1202,7 @@ _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, double *x2, double *y2, cairo_bool_t *is_tight); -cairo_private cairo_status_t +cairo_private void _cairo_gstate_path_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, @@ -1166,7 +1246,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, double y, cairo_bool_t *inside_ret); -cairo_private cairo_status_t +cairo_private void _cairo_gstate_in_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, @@ -1273,22 +1353,29 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, int num_glyphs, cairo_path_fixed_t *path); +cairo_private cairo_status_t +_cairo_gstate_set_antialias (cairo_gstate_t *gstate, + cairo_antialias_t antialias); + +cairo_private cairo_antialias_t +_cairo_gstate_get_antialias (cairo_gstate_t *gstate); + cairo_private cairo_bool_t -_cairo_operator_bounded_by_mask (cairo_operator_t op); +_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const; cairo_private cairo_bool_t -_cairo_operator_bounded_by_source (cairo_operator_t op); +_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const; /* cairo-color.c */ cairo_private const cairo_color_t * -_cairo_stock_color (cairo_stock_t stock); +_cairo_stock_color (cairo_stock_t stock) cairo_pure; #define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE) #define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK) #define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT) cairo_private uint16_t -_cairo_color_double_to_short (double d); +_cairo_color_double_to_short (double d) cairo_const; cairo_private void _cairo_color_init (cairo_color_t *color); @@ -1322,11 +1409,11 @@ _cairo_color_get_rgba_premultiplied (cairo_color_t *color, cairo_private cairo_bool_t _cairo_color_equal (const cairo_color_t *color_a, - const cairo_color_t *color_b); + const cairo_color_t *color_b) cairo_pure; /* cairo-font-face.c */ -extern const cairo_private cairo_toy_font_face_t _cairo_font_face_nil; +extern const cairo_private cairo_font_face_t _cairo_font_face_nil; cairo_private void _cairo_font_face_init (cairo_font_face_t *font_face, @@ -1348,9 +1435,9 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); /* cairo-font-face-twin.c */ -cairo_private cairo_font_face_t * -_cairo_font_face_twin_create (cairo_font_slant_t slant, - cairo_font_weight_t weight); +cairo_private cairo_status_t +_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); /* cairo-font-face-twin-data.c */ @@ -1384,6 +1471,12 @@ _cairo_validate_text_clusters (const char *utf8, int num_clusters, cairo_text_cluster_flags_t cluster_flags); +cairo_private cairo_status_t +_cairo_intern_string (const char **str_inout, int len); + +cairo_private void +_cairo_intern_string_reset_static_data (void); + /* cairo-path-fixed.c */ cairo_private void _cairo_path_fixed_init (cairo_path_fixed_t *path); @@ -1450,17 +1543,17 @@ _cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, typedef cairo_status_t (cairo_path_fixed_move_to_func_t) (void *closure, - cairo_point_t *point); + const cairo_point_t *point); typedef cairo_status_t (cairo_path_fixed_line_to_func_t) (void *closure, - cairo_point_t *point); + const cairo_point_t *point); typedef cairo_status_t (cairo_path_fixed_curve_to_func_t) (void *closure, - cairo_point_t *p0, - cairo_point_t *p1, - cairo_point_t *p2); + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2); typedef cairo_status_t (cairo_path_fixed_close_path_func_t) (void *closure); @@ -1488,11 +1581,24 @@ _cairo_path_fixed_append (cairo_path_fixed_t *path, const cairo_path_fixed_t *other, cairo_direction_t dir); -cairo_private cairo_status_t +cairo_private void +_cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_approximate_stroke_extents (cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + cairo_rectangle_int_t *extents); + +cairo_private void _cairo_path_fixed_bounds (cairo_path_fixed_t *path, double *x1, double *y1, - double *x2, double *y2, - double tolerance); + double *x2, double *y2); cairo_private void _cairo_path_fixed_transform (cairo_path_fixed_t *path, @@ -1509,6 +1615,18 @@ cairo_private cairo_bool_t _cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path, cairo_box_t *box); +cairo_private cairo_bool_t +_cairo_path_fixed_is_region (cairo_path_fixed_t *path); + +/* cairo-path-in-fill.c */ +cairo_private void +_cairo_path_fixed_in_fill (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + double x, + double y, + cairo_bool_t *is_inside); + /* cairo-path-fill.c */ cairo_private cairo_status_t _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, @@ -1520,8 +1638,8 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, cairo_private cairo_status_t _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps); @@ -1581,7 +1699,7 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, cairo_private cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_surface_t *surface, int source_x, int source_y, @@ -1653,6 +1771,10 @@ cairo_private cairo_surface_t * _cairo_surface_create_in_error (cairo_status_t status); cairo_private cairo_status_t +_cairo_surface_copy_mime_data (cairo_surface_t *dst, + cairo_surface_t *src); + +cairo_private cairo_status_t _cairo_surface_set_error (cairo_surface_t *surface, cairo_status_t status); @@ -1676,12 +1798,12 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, cairo_private cairo_surface_t * _cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, - cairo_solid_pattern_t *solid_pattern); + const cairo_solid_pattern_t *solid_pattern); cairo_private cairo_int_status_t _cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, cairo_surface_t *solid_surface, - cairo_solid_pattern_t *solid_pattern); + const cairo_solid_pattern_t *solid_pattern); cairo_private void _cairo_surface_init (cairo_surface_t *surface, @@ -1697,8 +1819,8 @@ _cairo_surface_get_clip_mode (cairo_surface_t *surface); cairo_private cairo_status_t _cairo_surface_composite (cairo_operator_t op, - cairo_pattern_t *src, - cairo_pattern_t *mask, + const cairo_pattern_t *src, + const cairo_pattern_t *mask, cairo_surface_t *dst, int src_x, int src_y, @@ -1734,54 +1856,59 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source); + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask); + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_operator_t fill_op, - cairo_pattern_t *fill_source, + const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, - cairo_pattern_t *stroke_source, + const cairo_pattern_t *stroke_source, cairo_stroke_style_t *stroke_style, cairo_matrix_t *stroke_ctm, cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, - cairo_antialias_t stroke_antialias); + cairo_antialias_t stroke_antialias, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_stroke (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const 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_antialias_t antialias, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_fill (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias); + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -1789,11 +1916,12 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font); + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *dst, cairo_antialias_t antialias, int src_x, @@ -1805,6 +1933,22 @@ _cairo_surface_composite_trapezoids (cairo_operator_t op, cairo_trapezoid_t *traps, int ntraps); +cairo_private cairo_span_renderer_t * +_cairo_surface_create_span_renderer ( + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects); + +cairo_private cairo_bool_t +_cairo_surface_check_span_renderer ( + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *dst, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *rects); + cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, @@ -1832,6 +1976,7 @@ _cairo_surface_release_dest_image (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -1843,6 +1988,18 @@ _cairo_surface_clone_similar (cairo_surface_t *surface, cairo_private cairo_surface_t * _cairo_surface_snapshot (cairo_surface_t *surface); +cairo_private cairo_status_t +_cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func); + +cairo_private cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend); + +cairo_private void +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot); + cairo_private cairo_bool_t _cairo_surface_is_similar (cairo_surface_t *surface_a, cairo_surface_t *surface_b, @@ -1885,7 +2042,7 @@ _cairo_surface_get_extents (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, - cairo_pattern_t *pattern, + const cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, @@ -1938,7 +2095,7 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface, double sy); cairo_private cairo_bool_t -_cairo_surface_has_device_transform (cairo_surface_t *surface); +_cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure; /* cairo-image-surface.c */ @@ -1978,7 +2135,7 @@ _cairo_surface_has_device_transform (cairo_surface_t *surface); /* pixman-required stride alignment in bytes. */ #define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) #define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \ - (((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & ~(CAIRO_STRIDE_ALIGNMENT-1) + ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT) #define CAIRO_CONTENT_VALID(content) ((content) && \ (((content) & ~(CAIRO_CONTENT_COLOR | \ @@ -1987,13 +2144,13 @@ _cairo_surface_has_device_transform (cairo_surface_t *surface); == 0)) cairo_private int -_cairo_format_bits_per_pixel (cairo_format_t format); +_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; cairo_private cairo_format_t -_cairo_format_from_content (cairo_content_t content); +_cairo_format_from_content (cairo_content_t content) cairo_const; cairo_private cairo_content_t -_cairo_content_from_format (cairo_format_t format); +_cairo_content_from_format (cairo_format_t format) cairo_const; cairo_private cairo_surface_t * _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, @@ -2050,30 +2207,30 @@ _cairo_image_surface_set_clip_region (void *abstract_surface, cairo_region_t *region); cairo_private cairo_image_surface_t * -_cairo_image_surface_clone (cairo_image_surface_t *surface, - cairo_format_t format); +_cairo_image_surface_coerce (cairo_image_surface_t *surface, + cairo_format_t format); cairo_private cairo_image_transparency_t _cairo_image_analyze_transparency (cairo_image_surface_t *image); cairo_private cairo_bool_t -_cairo_surface_is_image (const cairo_surface_t *surface); +_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure; cairo_private cairo_bool_t -_cairo_surface_is_meta (const cairo_surface_t *surface); +_cairo_surface_is_meta (const cairo_surface_t *surface) cairo_pure; /* cairo-pen.c */ cairo_private cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, double tolerance, - cairo_matrix_t *ctm); + const cairo_matrix_t *ctm); cairo_private void _cairo_pen_init_empty (cairo_pen_t *pen); cairo_private cairo_status_t -_cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other); +_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other); cairo_private void _cairo_pen_fini (cairo_pen_t *pen); @@ -2088,21 +2245,40 @@ _cairo_pen_add_points_for_slopes (cairo_pen_t *pen, cairo_point_t *c, cairo_point_t *d); -cairo_private void -_cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen, - cairo_slope_t *slope, - int *active); +cairo_private int +_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope); -cairo_private void -_cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen, - cairo_slope_t *slope, - int *active); +cairo_private int +_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope); + +typedef struct _cairo_pen_stroke_spline { + cairo_pen_t pen; + cairo_spline_t spline; + cairo_polygon_t polygon; + cairo_point_t last_point; + cairo_point_t forward_hull_point; + cairo_point_t backward_hull_point; + int forward_vertex; + int backward_vertex; +} cairo_pen_stroke_spline_t; + +cairo_private cairo_int_status_t +_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker, + const cairo_pen_t *pen, + const cairo_point_t *a, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d); cairo_private cairo_status_t -_cairo_pen_stroke_spline (cairo_pen_t *pen, - cairo_spline_t *spline, - double tolerance, - cairo_traps_t *traps); +_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t *pen, + double tolerance, + cairo_traps_t *traps); + +cairo_private void +_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker); /* cairo-polygon.c */ cairo_private void @@ -2112,6 +2288,12 @@ cairo_private void _cairo_polygon_fini (cairo_polygon_t *polygon); cairo_private void +_cairo_polygon_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + int dir); + +cairo_private void _cairo_polygon_move_to (cairo_polygon_t *polygon, const cairo_point_t *point); @@ -2125,18 +2307,21 @@ _cairo_polygon_close (cairo_polygon_t *polygon); #define _cairo_polygon_status(P) (P)->status /* cairo-spline.c */ -cairo_private cairo_int_status_t +cairo_private cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, - const cairo_point_t *a, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d); + cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *a, const cairo_point_t *b, + const cairo_point_t *c, const cairo_point_t *d); cairo_private cairo_status_t _cairo_spline_decompose (cairo_spline_t *spline, double tolerance); -cairo_private void -_cairo_spline_fini (cairo_spline_t *spline); +cairo_private cairo_status_t +_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *p0, const cairo_point_t *p1, + const cairo_point_t *p2, const cairo_point_t *p3); /* cairo-matrix.c */ cairo_private void @@ -2157,34 +2342,37 @@ _cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, cairo_bool_t *is_tight); cairo_private cairo_bool_t -_cairo_matrix_is_invertible (const cairo_matrix_t *matrix); +_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure; cairo_private double -_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix); +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure; cairo_private cairo_status_t _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, - double *sx, double *sy, int x_major); + double *sx, double *sy, int x_major); cairo_private cairo_bool_t -_cairo_matrix_is_identity (const cairo_matrix_t *matrix); +_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure; cairo_private cairo_bool_t -_cairo_matrix_is_translation (const cairo_matrix_t *matrix); +_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure; cairo_private cairo_bool_t _cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, int *itx, int *ity); cairo_private cairo_bool_t -_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix); +_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure; cairo_private double -_cairo_matrix_transformed_circle_major_axis(cairo_matrix_t *matrix, double radius); +_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, + double radius) cairo_pure; cairo_private void _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform); + pixman_transform_t *pixman_transform, + double xc, + double yc); /* cairo-traps.c */ cairo_private void @@ -2246,7 +2434,7 @@ _cairo_traps_extents (const cairo_traps_t *traps, cairo_private cairo_int_status_t _cairo_traps_extract_region (const cairo_traps_t *tr, - cairo_region_t *region); + cairo_region_t **region); cairo_private cairo_status_t _cairo_traps_path (const cairo_traps_t *traps, @@ -2266,7 +2454,8 @@ _cairo_slope_init (cairo_slope_t *slope, const cairo_point_t *b); cairo_private int -_cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b); +_cairo_slope_compare (const cairo_slope_t *a, + const cairo_slope_t *b) cairo_pure; /* cairo-pattern.c */ @@ -2317,53 +2506,78 @@ _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); cairo_private cairo_bool_t _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern); +enum { + CAIRO_PATTERN_ACQUIRE_NONE = 0x0, + CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1, +}; cairo_private cairo_int_status_t -_cairo_pattern_acquire_surface (cairo_pattern_t *pattern, +_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, cairo_surface_t *dst, + cairo_content_t content, int x, int y, unsigned int width, unsigned int height, + unsigned int flags, cairo_surface_t **surface_out, cairo_surface_attributes_t *attributes); cairo_private void -_cairo_pattern_release_surface (cairo_pattern_t *pattern, +_cairo_pattern_release_surface (const cairo_pattern_t *pattern, cairo_surface_t *surface, cairo_surface_attributes_t *attributes); cairo_private cairo_int_status_t -_cairo_pattern_acquire_surfaces (cairo_pattern_t *src, - cairo_pattern_t *mask, +_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, + const cairo_pattern_t *mask, cairo_surface_t *dst, + cairo_content_t src_content, int src_x, int src_y, int mask_x, int mask_y, unsigned int width, unsigned int height, + unsigned int flags, cairo_surface_t **src_out, cairo_surface_t **mask_out, cairo_surface_attributes_t *src_attributes, cairo_surface_attributes_t *mask_attributes); cairo_private cairo_status_t -_cairo_pattern_get_extents (cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents); +_cairo_pattern_get_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents); + +cairo_private unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern); + +cairo_private unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, + const cairo_pattern_t *b); cairo_private void _cairo_pattern_reset_static_data (void); -cairo_private cairo_status_t -_cairo_gstate_set_antialias (cairo_gstate_t *gstate, - cairo_antialias_t antialias); +/* cairo-region.c */ -cairo_private cairo_antialias_t -_cairo_gstate_get_antialias (cairo_gstate_t *gstate); +struct _cairo_region { + cairo_status_t status; + + pixman_region32_t rgn; +}; -/* cairo-region.c */ +cairo_private void +_cairo_region_init (cairo_region_t *region); -#include "cairo-region-private.h" +cairo_private void +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_private void +_cairo_region_fini (cairo_region_t *region); /* cairo-unicode.c */ @@ -2423,6 +2637,7 @@ slim_hidden_proto (cairo_font_options_set_hint_metrics); slim_hidden_proto (cairo_font_options_set_hint_style); slim_hidden_proto (cairo_font_options_set_subpixel_order); slim_hidden_proto (cairo_font_options_status); +slim_hidden_proto (cairo_format_stride_for_width); slim_hidden_proto (cairo_get_current_point); slim_hidden_proto (cairo_get_line_width); slim_hidden_proto (cairo_get_matrix); @@ -2436,7 +2651,6 @@ slim_hidden_proto (cairo_image_surface_get_data); slim_hidden_proto (cairo_image_surface_get_height); slim_hidden_proto (cairo_image_surface_get_stride); slim_hidden_proto (cairo_image_surface_get_width); -slim_hidden_proto (cairo_format_stride_for_width); slim_hidden_proto (cairo_line_to); slim_hidden_proto (cairo_mask); slim_hidden_proto (cairo_matrix_init); @@ -2453,19 +2667,15 @@ slim_hidden_proto (cairo_matrix_translate); slim_hidden_proto (cairo_move_to); slim_hidden_proto (cairo_new_path); slim_hidden_proto (cairo_paint); -slim_hidden_proto (cairo_path_extents); slim_hidden_proto (cairo_pattern_create_for_surface); slim_hidden_proto (cairo_pattern_create_rgb); slim_hidden_proto (cairo_pattern_create_rgba); slim_hidden_proto (cairo_pattern_destroy); slim_hidden_proto (cairo_pattern_get_extend); -slim_hidden_proto (cairo_pattern_get_type); slim_hidden_proto_no_warn (cairo_pattern_reference); slim_hidden_proto (cairo_pattern_set_matrix); slim_hidden_proto (cairo_pattern_status); slim_hidden_proto (cairo_pop_group); -slim_hidden_proto (cairo_pop_group_to_source); -slim_hidden_proto (cairo_push_group); slim_hidden_proto (cairo_push_group_with_content); slim_hidden_proto (cairo_rel_line_to); slim_hidden_proto (cairo_restore); @@ -2478,23 +2688,27 @@ slim_hidden_proto (cairo_scaled_font_get_ctm); slim_hidden_proto (cairo_scaled_font_get_font_face); slim_hidden_proto (cairo_scaled_font_get_font_matrix); slim_hidden_proto (cairo_scaled_font_get_font_options); -slim_hidden_proto (cairo_scaled_font_text_to_glyphs); slim_hidden_proto (cairo_scaled_font_glyph_extents); slim_hidden_proto_no_warn (cairo_scaled_font_reference); slim_hidden_proto (cairo_scaled_font_status); -slim_hidden_proto (cairo_set_font_size); +slim_hidden_proto (cairo_scaled_font_get_user_data); +slim_hidden_proto (cairo_scaled_font_set_user_data); +slim_hidden_proto (cairo_scaled_font_text_to_glyphs); slim_hidden_proto (cairo_set_font_options); +slim_hidden_proto (cairo_set_font_size); slim_hidden_proto (cairo_set_line_cap); slim_hidden_proto (cairo_set_line_join); slim_hidden_proto (cairo_set_line_width); slim_hidden_proto (cairo_set_matrix); slim_hidden_proto (cairo_set_operator); slim_hidden_proto (cairo_set_source); -slim_hidden_proto (cairo_set_source); +slim_hidden_proto (cairo_set_source_rgb); slim_hidden_proto (cairo_set_source_surface); +slim_hidden_proto (cairo_set_tolerance); slim_hidden_proto (cairo_status); slim_hidden_proto (cairo_stroke); slim_hidden_proto (cairo_stroke_preserve); +slim_hidden_proto (cairo_surface_copy_page); slim_hidden_proto (cairo_surface_create_similar); slim_hidden_proto (cairo_surface_destroy); slim_hidden_proto (cairo_surface_finish); @@ -2502,13 +2716,14 @@ slim_hidden_proto (cairo_surface_flush); slim_hidden_proto (cairo_surface_get_content); slim_hidden_proto (cairo_surface_get_device_offset); slim_hidden_proto (cairo_surface_get_font_options); +slim_hidden_proto (cairo_surface_get_mime_data); slim_hidden_proto (cairo_surface_get_type); slim_hidden_proto (cairo_surface_has_show_text_glyphs); slim_hidden_proto (cairo_surface_mark_dirty_rectangle); slim_hidden_proto_no_warn (cairo_surface_reference); slim_hidden_proto (cairo_surface_set_device_offset); slim_hidden_proto (cairo_surface_set_fallback_resolution); -slim_hidden_proto (cairo_surface_copy_page); +slim_hidden_proto (cairo_surface_set_mime_data); slim_hidden_proto (cairo_surface_show_page); slim_hidden_proto (cairo_surface_status); slim_hidden_proto (cairo_text_cluster_allocate); @@ -2516,11 +2731,33 @@ slim_hidden_proto (cairo_text_cluster_free); slim_hidden_proto (cairo_toy_font_face_create); slim_hidden_proto (cairo_toy_font_face_get_slant); slim_hidden_proto (cairo_toy_font_face_get_weight); +slim_hidden_proto (cairo_translate); +slim_hidden_proto (cairo_transform); slim_hidden_proto (cairo_user_font_face_create); slim_hidden_proto (cairo_user_font_face_set_init_func); slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); +slim_hidden_proto (cairo_user_to_device); +slim_hidden_proto (cairo_user_to_device_distance); slim_hidden_proto (cairo_version_string); +slim_hidden_proto (cairo_region_create); +slim_hidden_proto (cairo_region_create_rectangle); +slim_hidden_proto (cairo_region_copy); +slim_hidden_proto (cairo_region_destroy); +slim_hidden_proto (cairo_region_status); +slim_hidden_proto (cairo_region_get_extents); +slim_hidden_proto (cairo_region_num_rectangles); +slim_hidden_proto (cairo_region_get_rectangle); +slim_hidden_proto (cairo_region_is_empty); +slim_hidden_proto (cairo_region_contains_rectangle); +slim_hidden_proto (cairo_region_contains_point); +slim_hidden_proto (cairo_region_translate); +slim_hidden_proto (cairo_region_subtract); +slim_hidden_proto (cairo_region_subtract_rectangle); +slim_hidden_proto (cairo_region_intersect); +slim_hidden_proto (cairo_region_intersect_rectangle); +slim_hidden_proto (cairo_region_union); +slim_hidden_proto (cairo_region_union_rectangle); #if CAIRO_HAS_PNG_FUNCTIONS @@ -2536,4 +2773,19 @@ CAIRO_END_DECLS #include "cairo-malloc-private.h" #include "cairo-hash-private.h" +#if HAVE_VALGRIND +#include <memcheck.h> + +#define VG(x) x + +cairo_private void +_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); + +#else + +#define VG(x) +#define _cairo_debug_check_image_surface_is_defined(X) + +#endif + #endif diff --git a/src/check-doc-syntax.sh b/src/check-doc-syntax.sh index abf526d..a5c8462 100755 --- a/src/check-doc-syntax.sh +++ b/src/check-doc-syntax.sh @@ -64,7 +64,7 @@ if echo $FILES | xargs grep . /dev/null | sed -e '/<programlisting>/,/<\/program echo " '$func_regexp'" fi >&2 -note_regexp='NOTE' +note_regexp='\<NOTE\>' if echo $FILES | xargs grep "$note_regexp" /dev/null; then stat=1 echo Error: some source files contain the string 'NOTE'. diff --git a/src/test-fallback-surface.c b/src/test-fallback-surface.c index 883941d..2a7f148 100644 --- a/src/test-fallback-surface.c +++ b/src/test-fallback-surface.c @@ -79,7 +79,7 @@ _cairo_test_fallback_surface_create (cairo_content_t content, return backing; surface = malloc (sizeof (test_fallback_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { cairo_surface_destroy (backing); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } @@ -172,6 +172,7 @@ _test_fallback_surface_release_dest_image (void *abstract_surface, static cairo_status_t _test_fallback_surface_clone_similar (void *abstract_surface, cairo_surface_t *src, + cairo_content_t content, int src_x, int src_y, int width, @@ -214,6 +215,8 @@ static const cairo_surface_backend_t test_fallback_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* set_clip_region */ diff --git a/src/test-fallback16-surface.c b/src/test-fallback16-surface.c new file mode 100644 index 0000000..07c0610 --- /dev/null +++ b/src/test-fallback16-surface.c @@ -0,0 +1,236 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +/* This isn't a "real" surface, but just something to be used by the + * test suite to force use of a non-standard pixman image fallback - as + * may be exposed by xlib fallbacks with weird xservers, for example. + */ + +#include "cairoint.h" + +#include "test-fallback16-surface.h" + +typedef struct _test_fallback16_surface { + cairo_surface_t base; + + /* This is a cairo_image_surface to hold the actual contents. */ + cairo_surface_t *backing; +} test_fallback16_surface_t; + +static const cairo_surface_backend_t test_fallback16_surface_backend; + +slim_hidden_proto (_cairo_test_fallback16_surface_create); + +cairo_surface_t * +_cairo_test_fallback16_surface_create (cairo_content_t content, + int width, + int height) +{ + test_fallback16_surface_t *surface; + cairo_surface_t *backing; + pixman_format_code_t format; + + format = content & CAIRO_CONTENT_ALPHA ? PIXMAN_a1r5g5b5: PIXMAN_r5g6b5; + + backing = _cairo_image_surface_create_with_pixman_format (NULL, format, + width, height, + -1); + if (cairo_surface_status (backing)) + return backing; + + surface = malloc (sizeof (test_fallback16_surface_t)); + if (unlikely (surface == NULL)) { + cairo_surface_destroy (backing); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base, + &test_fallback16_surface_backend, + content); + + surface->backing = backing; + + return &surface->base; +} +slim_hidden_def (_cairo_test_fallback16_surface_create); + +static cairo_surface_t * +_test_fallback16_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + assert (CAIRO_CONTENT_VALID (content)); + + return _cairo_test_fallback16_surface_create (content, width, height); +} + +static cairo_status_t +_test_fallback16_surface_finish (void *abstract_surface) +{ + test_fallback16_surface_t *surface = abstract_surface; + + cairo_surface_destroy (surface->backing); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_test_fallback16_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + test_fallback16_surface_t *surface = abstract_surface; + + return _cairo_surface_acquire_source_image (surface->backing, + image_out, image_extra); +} + +static void +_test_fallback16_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + test_fallback16_surface_t *surface = abstract_surface; + + _cairo_surface_release_source_image (surface->backing, + image, image_extra); +} + +static cairo_status_t +_test_fallback16_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect_out, + void **image_extra) +{ + test_fallback16_surface_t *surface = abstract_surface; + + return _cairo_surface_acquire_dest_image (surface->backing, + interest_rect, + image_out, + image_rect_out, + image_extra); +} + +static void +_test_fallback16_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + test_fallback16_surface_t *surface = abstract_surface; + + _cairo_surface_release_dest_image (surface->backing, + interest_rect, + image, + image_rect, + image_extra); +} + +static cairo_status_t +_test_fallback16_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_content_t content, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + test_fallback16_surface_t *surface = abstract_surface; + + if (src->backend == surface->base.backend) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } else { + return _cairo_surface_clone_similar (surface->backing, src, + content, + src_x, src_y, + width, height, + clone_offset_x, clone_offset_y, + clone_out); + } +} + +static cairo_int_status_t +_test_fallback16_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + test_fallback16_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->backing, rectangle); +} + +static const cairo_surface_backend_t test_fallback16_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + _test_fallback16_surface_create_similar, + _test_fallback16_surface_finish, + _test_fallback16_surface_acquire_source_image, + _test_fallback16_surface_release_source_image, + _test_fallback16_surface_acquire_dest_image, + _test_fallback16_surface_release_dest_image, + _test_fallback16_surface_clone_similar, + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + NULL, /* set_clip_region */ + NULL, /* intersect_clip_path */ + _test_fallback16_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 */ + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + NULL, /* show_glyphs */ + NULL /* snapshot */ +}; diff --git a/src/test-fallback16-surface.h b/src/test-fallback16-surface.h new file mode 100644 index 0000000..2c5b2ca --- /dev/null +++ b/src/test-fallback16-surface.h @@ -0,0 +1,52 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#ifndef TEST_FALLBACK16_SURFACE_H +#define TEST_FALLBACK16_SURFACE_H + +#include "cairo.h" + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +_cairo_test_fallback16_surface_create (cairo_content_t content, + int width, + int height); + +CAIRO_END_DECLS + +#endif /* TEST_FALLBACK16_SURFACE_H */ diff --git a/src/test-meta-surface.c b/src/test-meta-surface.c index 6038430..d5e14d7 100644 --- a/src/test-meta-surface.c +++ b/src/test-meta-surface.c @@ -77,7 +77,7 @@ _cairo_test_meta_surface_create (cairo_content_t content, cairo_status_t status; surface = malloc (sizeof (test_meta_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } @@ -194,38 +194,41 @@ _test_meta_surface_get_extents (void *abstract_surface, static cairo_int_status_t _test_meta_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { test_meta_surface_t *surface = abstract_surface; surface->image_reflects_meta = FALSE; - return _cairo_surface_paint (surface->meta, op, source); + return _cairo_surface_paint (surface->meta, op, source, extents); } static cairo_int_status_t _test_meta_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { test_meta_surface_t *surface = abstract_surface; surface->image_reflects_meta = FALSE; - return _cairo_surface_mask (surface->meta, op, source, mask); + return _cairo_surface_mask (surface->meta, op, source, mask, extents); } static cairo_int_status_t -_test_meta_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_meta_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const 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_rectangle_int_t *extents) { test_meta_surface_t *surface = abstract_surface; @@ -234,17 +237,18 @@ _test_meta_surface_stroke (void *abstract_surface, return _cairo_surface_stroke (surface->meta, op, source, path, style, ctm, ctm_inverse, - tolerance, antialias); + tolerance, antialias, extents); } static cairo_int_status_t _test_meta_surface_fill (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, - cairo_antialias_t antialias) + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { test_meta_surface_t *surface = abstract_surface; @@ -252,7 +256,7 @@ _test_meta_surface_fill (void *abstract_surface, return _cairo_surface_fill (surface->meta, op, source, path, fill_rule, - tolerance, antialias); + tolerance, antialias, extents); } static cairo_bool_t @@ -266,7 +270,7 @@ _test_meta_surface_has_show_text_glyphs (void *abstract_surface) static cairo_int_status_t _test_meta_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -274,7 +278,8 @@ _test_meta_surface_show_text_glyphs (void *abstract_surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) { test_meta_surface_t *surface = abstract_surface; @@ -284,7 +289,7 @@ _test_meta_surface_show_text_glyphs (void *abstract_surface, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - scaled_font); + scaled_font, extents); } @@ -308,6 +313,8 @@ static const cairo_surface_backend_t test_meta_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ _test_meta_surface_show_page, NULL, /* set_clip_region */ @@ -329,6 +336,7 @@ static const cairo_surface_backend_t test_meta_surface_backend = { NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ _test_meta_surface_has_show_text_glyphs, _test_meta_surface_show_text_glyphs }; diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c index 354f95d..d42700c 100644 --- a/src/test-paginated-surface.c +++ b/src/test-paginated-surface.c @@ -80,7 +80,7 @@ _cairo_test_paginated_surface_create_for_data (unsigned char *data, return target; surface = malloc (sizeof (test_paginated_surface_t)); - if (surface == NULL) { + if (unlikely (surface == NULL)) { cairo_surface_destroy (target); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } @@ -169,40 +169,43 @@ _test_paginated_surface_get_extents (void *abstract_surface, static cairo_int_status_t _test_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source) + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) { 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); + return _cairo_surface_paint (surface->target, op, source, extents); } static cairo_int_status_t _test_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, - cairo_pattern_t *mask) + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) { test_paginated_surface_t *surface = abstract_surface; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return CAIRO_STATUS_SUCCESS; - return _cairo_surface_mask (surface->target, op, source, mask); + return _cairo_surface_mask (surface->target, op, source, mask, extents); } 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_stroke (void *abstract_surface, + cairo_operator_t op, + const 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_rectangle_int_t *extents) { test_paginated_surface_t *surface = abstract_surface; @@ -212,17 +215,18 @@ _test_paginated_surface_stroke (void *abstract_surface, return _cairo_surface_stroke (surface->target, op, source, path, style, ctm, ctm_inverse, - tolerance, antialias); + tolerance, antialias, extents); } 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_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) { test_paginated_surface_t *surface = abstract_surface; @@ -231,7 +235,7 @@ _test_paginated_surface_fill (void *abstract_surface, return _cairo_surface_fill (surface->target, op, source, path, fill_rule, - tolerance, antialias); + tolerance, antialias, extents); } static cairo_bool_t @@ -245,7 +249,7 @@ _test_paginated_surface_has_show_text_glyphs (void *abstract_surface) static cairo_int_status_t _test_paginated_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, - cairo_pattern_t *source, + const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, @@ -253,7 +257,8 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) { test_paginated_surface_t *surface = abstract_surface; @@ -264,7 +269,7 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - scaled_font); + scaled_font, extents); } @@ -293,6 +298,8 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { NULL, /* composite */ NULL, /* fill_rectangles */ NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _test_paginated_surface_set_clip_region, @@ -319,6 +326,7 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { NULL, /* reset */ NULL, /* fill_stroke */ NULL, /* create_solid_pattern_surface */ + NULL, /* can_repaint_solid_pattern_surface */ _test_paginated_surface_has_show_text_glyphs, _test_paginated_surface_show_text_glyphs |