diff options
Diffstat (limited to 'src')
128 files changed, 8960 insertions, 16604 deletions
diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index c03ef71a8..000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,99 +0,0 @@ -# Note: All source files are listed in Makefile.sources. - -include $(top_srcdir)/build/Makefile.am.common -include $(srcdir)/Makefile.am.features - -EXTRA_DIST += Makefile.win32 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 - -$(top_builddir)/config.h: $(top_srcdir)/config.h.in - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) config.h - -cairoincludedir = $(includedir)/cairo -cairoinclude_HEADERS = $(enabled_cairo_headers) - -lib_LTLIBRARIES = libcairo.la - -libcairo_la_SOURCES = \ - $(enabled_cairo_headers) \ - $(enabled_cairo_private) \ - $(enabled_cairo_sources) \ - $(NULL) -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 -nodist_cairoinclude_HEADERS = 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/$@ - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = $(enabled_cairo_pkgconf) - -CLEANFILES += cairo.def -cairo.def: cairo-features.h $(enabled_cairo_headers) - @echo Generating $@ - @(echo EXPORTS; \ - (cd $(srcdir); cat $(enabled_cairo_headers) || echo 'cairo_ERROR ()' ) | \ - $(EGREP) -v '^# *include' | \ - ( cat cairo-features.h - | $(CPP) -D__cplusplus - || echo 'cairo_ERROR ()' ) | \ - $(EGREP) '^cairo_.* \(' | \ - sed -e 's/[ ].*//' | \ - sort; \ - echo LIBRARY libcairo-$(CAIRO_VERSION_SONUM).dll; \ - ) >$@ - @ ! grep -q cairo_ERROR $@ || ($(RM) $@; false) - -TESTS_ENVIRONMENT = \ - srcdir="$(srcdir)" \ - MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ - all_cairo_files="$(all_cairo_files)" \ - all_cairo_headers="$(all_cairo_headers)" \ - all_cairo_private="$(all_cairo_private)" \ - all_cairo_sources="$(all_cairo_sources)" \ - enabled_cairo_headers="$(enabled_cairo_headers)" \ - enabled_cairo_private="$(enabled_cairo_private)" \ - enabled_cairo_sources="$(enabled_cairo_sources)" \ - $(NULL) -TESTS_SH = \ - check-def.sh \ - check-doc-syntax.sh \ - check-headers.sh \ - check-plt.sh \ - check-preprocessor-syntax.sh \ - $(NULL) -TESTS += $(TESTS_SH) -if CROSS_COMPILING -else -TESTS += check-link$(EXEEXT) -endif - -EXTRA_DIST += $(TESTS_SH) check-has-hidden-symbols.c check-doc-syntax.awk -check_PROGRAMS += check-link -check_link_LDADD = libcairo.la - -check: headers-standalone - -PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) -COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS) - -# The pre-processed result is used by check-{def,plt}.sh to determine whether -# cairo has been compiled with symbol hiding. -.c.i: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h - $(CPP) $(PREPROCESS_ARGS) $< -o $@ -.c.s: $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) cairoint.h $(top_builddir)/config.h - $(CC) $(COMPILE_ARGS) $< -S -o $@ - -include $(srcdir)/Makefile.am.analysis diff --git a/src/Makefile.am.analysis b/src/Makefile.am.analysis deleted file mode 100644 index 63bb844df..000000000 --- a/src/Makefile.am.analysis +++ /dev/null @@ -1,35 +0,0 @@ - -SPARSE = sparse -sparse: - @echo Checking enabled sources with sparse checker - @status=true; for f in $(enabled_cairo_sources); do \ - echo $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f; \ - $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \ - done; $$status - -SPLINT = splint -badflag -splint: - @echo Checking enabled sources with splint checker - @status=true; for f in $(enabled_cairo_sources); do \ - echo $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f; \ - $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \ - done; $$status - -UNO = uno -uno: - @echo Checking enabled sources with uno checker - cd $(srcdir); $(UNO) $(PREPROCESS_ARGS) -DHAVE_CONFIG_H -U__GNUC__ $(enabled_cairo_sources) - -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 " CHECK $$f"; \ - echo "#include \"$(srcdir)/$$f\"" > headers-standalone-tmp.c; \ - echo "int main(int argc, char * argv[]) { return 0; }" >> headers-standalone-tmp.c; \ - $(COMPILE) -o headers-standalone-tmp headers-standalone-tmp.c || status=false; \ - $(RM) headers-standalone-tmp headers-standalone-tmp.c; \ - done; $$status - @touch $@ -CLEANFILES += headers-standalone - -analysis: all headers-standalone sparse splint uno diff --git a/src/Makefile.sources b/src/Makefile.sources deleted file mode 100644 index 9328fca97..000000000 --- a/src/Makefile.sources +++ /dev/null @@ -1,405 +0,0 @@ -# Makefile.sources -# -# This file is the canonical location listing all the source files used -# to build the cairo library. Every source file is categorized as one of: -# -# * public header file -# * private header file (must end in -private.h except for cairoint.h) -# * source code file -# -# Every source file should be specified exactly once, grouped with the -# feature that uses the source file. If more than one feature use the -# file (like pdf_operators or font_subset files), the files should be -# appended to to the base cairo files, and the code inside them -# enabled/disabled using C preprocessor macros defined in cairoint.h. -# See how pdf_operators or font_subset are handled. -# -# The sources are picked up according to the configured features -# by the generated file Makefile.am.features or Makefile.win32.features. -# -# These are a few special source files. Those are not included in this -# file to not confuse build systems. Each build system must handle them -# separately. These files include: -# -# * cairo-features.h: -# This file is generated by configure and includes macros signifying -# which features are enabled. This file should be installed like -# other public headers, but should NOT be distributed in the cairo -# distribution. -# -# * cairo-supported-features.h: -# This file is generated by configure and includes macros signifying -# all supported features. This is used by gtk-doc to generate -# documentation for all those macros, enabled or not. -# This file is NOT used during the build of the library and should -# NOT be installed or distributed. -# -# Please follow the strict syntax of this file, including keeping file -# lists sorted. -# - -cairo_headers = cairo.h cairo-version.h cairo-deprecated.h -cairo_private = \ - cairoint.h \ - cairo-analysis-surface-private.h \ - cairo-arc-private.h \ - cairo-array-private.h \ - cairo-atomic-private.h \ - cairo-backend-private.h \ - cairo-box-inline.h \ - cairo-boxes-private.h \ - cairo-cache-private.h \ - cairo-clip-inline.h \ - cairo-clip-private.h \ - cairo-combsort-inline.h \ - cairo-compiler-private.h \ - cairo-composite-rectangles-private.h \ - cairo-compositor-private.h \ - cairo-contour-inline.h \ - cairo-contour-private.h \ - cairo-damage-private.h \ - cairo-default-context-private.h \ - cairo-device-private.h \ - cairo-error-inline.h \ - cairo-error-private.h \ - cairo-fixed-private.h \ - cairo-fixed-type-private.h \ - cairo-fontconfig-private.h \ - cairo-freed-pool-private.h \ - cairo-freelist-private.h \ - cairo-freelist-type-private.h \ - cairo-gstate-private.h \ - cairo-hash-private.h \ - cairo-image-info-private.h \ - cairo-image-surface-inline.h \ - cairo-image-surface-private.h \ - cairo-line-inline.h \ - cairo-line-private.h \ - cairo-list-inline.h \ - cairo-list-private.h \ - cairo-malloc-private.h \ - cairo-mempool-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-pattern-inline.h \ - cairo-pattern-private.h \ - cairo-pixman-private.h \ - cairo-private.h \ - cairo-recording-surface-inline.h \ - cairo-recording-surface-private.h \ - cairo-reference-count-private.h \ - cairo-region-private.h \ - cairo-rtree-private.h \ - cairo-scaled-font-private.h \ - cairo-slope-private.h \ - cairo-spans-compositor-private.h \ - cairo-spans-private.h \ - cairo-stroke-dash-private.h \ - cairo-surface-backend-private.h \ - cairo-surface-clipper-private.h \ - cairo-surface-fallback-private.h \ - cairo-surface-inline.h \ - cairo-surface-observer-inline.h \ - cairo-surface-observer-private.h \ - cairo-surface-offset-private.h \ - cairo-surface-private.h \ - cairo-surface-snapshot-inline.h \ - cairo-surface-snapshot-private.h \ - cairo-surface-subsurface-inline.h \ - cairo-surface-subsurface-private.h \ - cairo-surface-wrapper-private.h \ - cairo-time-private.h \ - cairo-traps-private.h \ - cairo-tristrip-private.h \ - cairo-types-private.h \ - cairo-user-font-private.h \ - cairo-wideint-private.h \ - cairo-wideint-type-private.h \ - $(NULL) -cairo_sources = \ - cairo-analysis-surface.c \ - cairo-arc.c \ - cairo-array.c \ - cairo-atomic.c \ - cairo-base64-stream.c \ - cairo-base85-stream.c \ - cairo-bentley-ottmann-rectangular.c \ - cairo-bentley-ottmann-rectilinear.c \ - cairo-bentley-ottmann.c \ - cairo-botor-scan-converter.c \ - cairo-boxes-intersect.c \ - cairo-boxes.c \ - cairo-cache.c \ - cairo-clip-boxes.c \ - cairo-clip-polygon.c \ - cairo-clip-region.c \ - cairo-clip-surface.c \ - cairo-clip-tor-scan-converter.c \ - cairo-clip.c \ - cairo-color.c \ - cairo-composite-rectangles.c \ - cairo-compositor.c \ - cairo-contour.c \ - cairo-damage.c \ - cairo-debug.c \ - cairo-default-context.c \ - cairo-device.c \ - cairo-error.c \ - cairo-fallback-compositor.c \ - cairo-fixed.c \ - cairo-font-face-twin-data.c \ - cairo-font-face-twin.c \ - cairo-font-face.c \ - cairo-font-options.c \ - cairo-freed-pool.c \ - cairo-freelist.c \ - cairo-gstate.c \ - cairo-hash.c \ - cairo-hull.c \ - cairo-image-compositor.c \ - cairo-image-info.c \ - cairo-image-source.c \ - cairo-image-surface.c \ - cairo-line.c \ - cairo-lzw.c \ - cairo-mask-compositor.c \ - cairo-matrix.c \ - cairo-mempool.c \ - cairo-mesh-pattern-rasterizer.c \ - cairo-misc.c \ - cairo-mono-scan-converter.c \ - cairo-mutex.c \ - cairo-no-compositor.c \ - cairo-observer.c \ - cairo-output-stream.c \ - cairo-paginated-surface.c \ - cairo-path-bounds.c \ - cairo-path-fill.c \ - cairo-path-fixed.c \ - cairo-path-in-fill.c \ - cairo-path-stroke-boxes.c \ - cairo-path-stroke-polygon.c \ - cairo-path-stroke-traps.c \ - cairo-path-stroke-tristrip.c \ - cairo-path-stroke.c \ - cairo-path.c \ - cairo-pattern.c \ - cairo-pen.c \ - cairo-polygon-intersect.c \ - cairo-polygon-reduce.c \ - cairo-polygon.c \ - cairo-raster-source-pattern.c \ - cairo-recording-surface.c \ - cairo-rectangle.c \ - cairo-rectangular-scan-converter.c \ - cairo-region.c \ - cairo-rtree.c \ - cairo-scaled-font.c \ - cairo-shape-mask-compositor.c \ - cairo-slope.c \ - cairo-spans-compositor.c \ - cairo-spans.c \ - cairo-spline.c \ - cairo-stroke-dash.c \ - cairo-stroke-style.c \ - cairo-surface-clipper.c \ - cairo-surface-fallback.c \ - cairo-surface-observer.c \ - cairo-surface-offset.c \ - cairo-surface-snapshot.c \ - cairo-surface-subsurface.c \ - cairo-surface-wrapper.c \ - cairo-surface.c \ - cairo-time.c \ - cairo-tor-scan-converter.c \ - cairo-tor22-scan-converter.c \ - cairo-toy-font-face.c \ - cairo-traps-compositor.c \ - cairo-traps.c \ - cairo-tristrip.c \ - cairo-unicode.c \ - cairo-user-font.c \ - cairo-version.c \ - cairo-wideint.c \ - cairo.c \ - $(NULL) - -_cairo_font_subset_private = \ - cairo-scaled-font-subsets-private.h \ - cairo-truetype-subset-private.h \ - cairo-type1-private.h \ - cairo-type3-glyph-surface-private.h \ - $(NULL) -_cairo_font_subset_sources = \ - cairo-cff-subset.c \ - cairo-scaled-font-subsets.c \ - cairo-truetype-subset.c \ - cairo-type1-fallback.c \ - cairo-type1-glyph-names.c \ - cairo-type1-subset.c \ - cairo-type3-glyph-surface.c \ - $(NULL) -cairo_private += $(_cairo_font_subset_private) -cairo_sources += $(_cairo_font_subset_sources) - -cairo_egl_sources = -cairo_glx_sources = -cairo_wgl_sources = - -_cairo_pdf_operators_private = \ - cairo-pdf-operators-private.h \ - cairo-pdf-shading-private.h \ - cairo-tag-attributes-private.h \ - $(NULL) -_cairo_pdf_operators_sources = \ - cairo-pdf-operators.c \ - cairo-pdf-shading.c \ - cairo-tag-attributes.c \ - $(NULL) -cairo_private += $(_cairo_pdf_operators_private) -cairo_sources += $(_cairo_pdf_operators_sources) - -cairo_png_sources = cairo-png.c - -cairo_ps_headers = cairo-ps.h -cairo_ps_private = cairo-ps-surface-private.h -cairo_ps_sources = cairo-ps-surface.c - -_cairo_deflate_stream_sources = cairo-deflate-stream.c -cairo_sources += $(_cairo_deflate_stream_sources) - -cairo_pdf_headers = cairo-pdf.h -cairo_pdf_private = cairo-pdf-surface-private.h cairo-tag-stack-private.h -cairo_pdf_sources = cairo-pdf-surface.c cairo-pdf-interchange.c cairo-tag-stack.c - -cairo_svg_headers = cairo-svg.h -cairo_svg_private = cairo-svg-surface-private.h -cairo_svg_sources = cairo-svg-surface.c - -cairo_ft_headers = cairo-ft.h -cairo_ft_private = cairo-ft-private.h -cairo_ft_sources = cairo-ft-font.c - -# These are private, even though they look like public headers -cairo_test_surfaces_private = \ - test-compositor-surface.h \ - test-compositor-surface-private.h \ - test-null-compositor-surface.h \ - test-paginated-surface.h \ - $(NULL) -cairo_test_surfaces_sources = \ - test-compositor-surface.c \ - test-null-compositor-surface.c \ - test-base-compositor-surface.c \ - test-paginated-surface.c \ - $(NULL) - -cairo_xlib_headers = cairo-xlib.h -cairo_xlib_private = \ - cairo-xlib-private.h \ - cairo-xlib-surface-private.h \ - cairo-xlib-xrender-private.h \ - $(NULL) -cairo_xlib_sources = \ - cairo-xlib-display.c \ - cairo-xlib-core-compositor.c \ - cairo-xlib-fallback-compositor.c \ - cairo-xlib-render-compositor.c \ - cairo-xlib-screen.c \ - cairo-xlib-source.c \ - cairo-xlib-surface.c \ - cairo-xlib-surface-shm.c \ - cairo-xlib-visual.c \ - cairo-xlib-xcb-surface.c \ - $(NULL) - -cairo_xlib_xrender_headers = cairo-xlib-xrender.h - -cairo_xcb_headers = cairo-xcb.h -cairo_xcb_private = cairo-xcb-private.h -cairo_xcb_sources = \ - cairo-xcb-connection.c \ - cairo-xcb-connection-core.c \ - cairo-xcb-connection-render.c \ - cairo-xcb-connection-shm.c \ - cairo-xcb-screen.c \ - cairo-xcb-shm.c \ - cairo-xcb-surface.c \ - cairo-xcb-surface-core.c \ - cairo-xcb-surface-render.c \ - cairo-xcb-resources.c \ - $(NULL) - -cairo_quartz_headers = cairo-quartz.h -cairo_quartz_private = cairo-quartz-private.h -cairo_quartz_sources = cairo-quartz-surface.c - -cairo_quartz_image_headers = cairo-quartz-image.h -cairo_quartz_image_sources = cairo-quartz-image-surface.c - -cairo_quartz_font_sources = cairo-quartz-font.c - -cairo_win32_headers = cairo-win32.h -cairo_win32_private = win32/cairo-win32-private.h -cairo_win32_sources = \ - win32/cairo-win32-debug.c \ - win32/cairo-win32-device.c \ - win32/cairo-win32-gdi-compositor.c \ - win32/cairo-win32-system.c \ - win32/cairo-win32-surface.c \ - win32/cairo-win32-display-surface.c \ - win32/cairo-win32-printing-surface.c \ - $(NULL) -cairo_win32_font_sources = \ - win32/cairo-win32-font.c \ - $(NULL) - -cairo_gl_headers = cairo-gl.h -cairo_gl_private = cairo-gl-private.h \ - cairo-gl-dispatch-private.h \ - cairo-gl-ext-def-private.h \ - cairo-gl-gradient-private.h - -cairo_gl_sources = cairo-gl-composite.c \ - cairo-gl-device.c \ - cairo-gl-dispatch.c \ - cairo-gl-glyphs.c \ - cairo-gl-gradient.c \ - cairo-gl-info.c \ - cairo-gl-msaa-compositor.c \ - cairo-gl-operand.c \ - cairo-gl-shaders.c \ - cairo-gl-source.c \ - cairo-gl-spans-compositor.c \ - cairo-gl-surface.c \ - cairo-gl-traps-compositor.c - -cairo_glesv2_headers = $(cairo_gl_headers) -cairo_glesv2_private = $(cairo_gl_private) -cairo_glesv2_sources = $(cairo_gl_sources) - -cairo_glesv3_headers = $(cairo_gl_headers) -cairo_glesv3_private = $(cairo_gl_private) -cairo_glesv3_sources = $(cairo_gl_sources) - -cairo_egl_sources += cairo-egl-context.c -cairo_glx_sources += cairo-glx-context.c -cairo_wgl_sources += cairo-wgl-context.c - -cairo_script_headers = cairo-script.h -cairo_script_private = cairo-script-private.h -cairo_script_sources = cairo-script-surface.c - -cairo_tee_headers = cairo-tee.h -cairo_tee_private = cairo-tee-surface-private.h -cairo_tee_sources = cairo-tee-surface.c - -cairo_xml_headers = cairo-xml.h -cairo_xml_sources = cairo-xml-surface.c diff --git a/src/Makefile.win32 b/src/Makefile.win32 deleted file mode 100644 index 9afefe2b7..000000000 --- a/src/Makefile.win32 +++ /dev/null @@ -1,28 +0,0 @@ -top_srcdir = .. -include $(top_srcdir)/build/Makefile.win32.common -include Makefile.win32.features - -SOURCES = $(enabled_cairo_sources) - -STATIC_SOURCES = cairo-system.c - -OBJECTS = $(patsubst %.c, $(CFG)/%.obj, $(SOURCES)) -OBJECTS_STATIC = $(patsubst %cairo-system.obj, %cairo-system-static.obj, $(OBJECTS)) - -static: inform $(CFG)/cairo-static.lib -dynamic: inform $(CFG)/cairo.dll - -$(CFG)/cairo.dll: $(OBJECTS) - @$(LD) $(CAIRO_LDFLAGS) -DLL -OUT:$@ $(CAIRO_LIBS) $(PIXMAN_LIBS) $(OBJECTS) - -$(CFG)/cairo-static.lib: $(OBJECTS_STATIC) - @$(AR) $(CAIRO_ARFLAGS) -OUT:$@ $(PIXMAN_LIBS) $(OBJECTS_STATIC) - -all: inform $(CFG)/cairo.dll $(CFG)/cairo-static.lib - @echo "Built successfully!" - @echo "You should copy the following files to a proper place now:" - @echo "" - @echo " src/cairo-features.h" - @for x in $(enabled_cairo_headers); do echo " src/$$x"; done - @echo " src/$(CFG)/cairo.dll" - @echo " src/$(CFG)/cairo-static.lib" diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features deleted file mode 100644 index fbc774d4f..000000000 --- a/src/Makefile.win32.features +++ /dev/null @@ -1,459 +0,0 @@ -# Generated by configure. Do not edit. - -ifeq ($(top_srcdir),) -include Makefile.sources -else -include $(top_srcdir)/src/Makefile.sources -endif - -supported_cairo_headers = $(cairo_headers) -unsupported_cairo_headers = -all_cairo_headers = $(cairo_headers) -all_cairo_private = $(cairo_private) -all_cairo_sources = $(cairo_sources) - -enabled_cairo_headers = $(cairo_headers) -enabled_cairo_private = $(cairo_private) -enabled_cairo_sources = $(cairo_sources) - -all_cairo_pkgconf = cairo.pc -enabled_cairo_pkgconf = cairo.pc - -supported_cairo_headers += $(cairo_xlib_headers) -all_cairo_headers += $(cairo_xlib_headers) -all_cairo_private += $(cairo_xlib_private) -all_cairo_sources += $(cairo_xlib_sources) -ifeq ($(CAIRO_HAS_XLIB_SURFACE),1) -enabled_cairo_headers += $(cairo_xlib_headers) -enabled_cairo_private += $(cairo_xlib_private) -enabled_cairo_sources += $(cairo_xlib_sources) -endif -all_cairo_pkgconf += cairo-xlib.pc -ifeq ($(CAIRO_HAS_XLIB_SURFACE),1) -enabled_cairo_pkgconf += cairo-xlib.pc -endif - -supported_cairo_headers += $(cairo_xlib_xrender_headers) -all_cairo_headers += $(cairo_xlib_xrender_headers) -all_cairo_private += $(cairo_xlib_xrender_private) -all_cairo_sources += $(cairo_xlib_xrender_sources) -ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1) -enabled_cairo_headers += $(cairo_xlib_xrender_headers) -enabled_cairo_private += $(cairo_xlib_xrender_private) -enabled_cairo_sources += $(cairo_xlib_xrender_sources) -endif -all_cairo_pkgconf += cairo-xlib-xrender.pc -ifeq ($(CAIRO_HAS_XLIB_XRENDER_SURFACE),1) -enabled_cairo_pkgconf += cairo-xlib-xrender.pc -endif - -supported_cairo_headers += $(cairo_xcb_headers) -all_cairo_headers += $(cairo_xcb_headers) -all_cairo_private += $(cairo_xcb_private) -all_cairo_sources += $(cairo_xcb_sources) -ifeq ($(CAIRO_HAS_XCB_SURFACE),1) -enabled_cairo_headers += $(cairo_xcb_headers) -enabled_cairo_private += $(cairo_xcb_private) -enabled_cairo_sources += $(cairo_xcb_sources) -endif -all_cairo_pkgconf += cairo-xcb.pc -ifeq ($(CAIRO_HAS_XCB_SURFACE),1) -enabled_cairo_pkgconf += cairo-xcb.pc -endif - -unsupported_cairo_headers += $(cairo_xlib_xcb_headers) -all_cairo_headers += $(cairo_xlib_xcb_headers) -all_cairo_private += $(cairo_xlib_xcb_private) -all_cairo_sources += $(cairo_xlib_xcb_sources) -ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_xlib_xcb_headers) -enabled_cairo_private += $(cairo_xlib_xcb_private) -enabled_cairo_sources += $(cairo_xlib_xcb_sources) -endif -all_cairo_pkgconf += cairo-xlib-xcb.pc -ifeq ($(CAIRO_HAS_XLIB_XCB_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-xlib-xcb.pc -endif - -supported_cairo_headers += $(cairo_xcb_shm_headers) -all_cairo_headers += $(cairo_xcb_shm_headers) -all_cairo_private += $(cairo_xcb_shm_private) -all_cairo_sources += $(cairo_xcb_shm_sources) -ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_xcb_shm_headers) -enabled_cairo_private += $(cairo_xcb_shm_private) -enabled_cairo_sources += $(cairo_xcb_shm_sources) -endif -all_cairo_pkgconf += cairo-xcb-shm.pc -ifeq ($(CAIRO_HAS_XCB_SHM_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-xcb-shm.pc -endif - -supported_cairo_headers += $(cairo_quartz_headers) -all_cairo_headers += $(cairo_quartz_headers) -all_cairo_private += $(cairo_quartz_private) -all_cairo_sources += $(cairo_quartz_sources) -ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1) -enabled_cairo_headers += $(cairo_quartz_headers) -enabled_cairo_private += $(cairo_quartz_private) -enabled_cairo_sources += $(cairo_quartz_sources) -endif -all_cairo_pkgconf += cairo-quartz.pc -ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1) -enabled_cairo_pkgconf += cairo-quartz.pc -endif - -supported_cairo_headers += $(cairo_quartz_font_headers) -all_cairo_headers += $(cairo_quartz_font_headers) -all_cairo_private += $(cairo_quartz_font_private) -all_cairo_sources += $(cairo_quartz_font_sources) -ifeq ($(CAIRO_HAS_QUARTZ_FONT),1) -enabled_cairo_headers += $(cairo_quartz_font_headers) -enabled_cairo_private += $(cairo_quartz_font_private) -enabled_cairo_sources += $(cairo_quartz_font_sources) -endif -all_cairo_pkgconf += cairo-quartz-font.pc -ifeq ($(CAIRO_HAS_QUARTZ_FONT),1) -enabled_cairo_pkgconf += cairo-quartz-font.pc -endif - -unsupported_cairo_headers += $(cairo_quartz_image_headers) -all_cairo_headers += $(cairo_quartz_image_headers) -all_cairo_private += $(cairo_quartz_image_private) -all_cairo_sources += $(cairo_quartz_image_sources) -ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1) -enabled_cairo_headers += $(cairo_quartz_image_headers) -enabled_cairo_private += $(cairo_quartz_image_private) -enabled_cairo_sources += $(cairo_quartz_image_sources) -endif -all_cairo_pkgconf += cairo-quartz-image.pc -ifeq ($(CAIRO_HAS_QUARTZ_IMAGE_SURFACE),1) -enabled_cairo_pkgconf += cairo-quartz-image.pc -endif - -supported_cairo_headers += $(cairo_win32_headers) -all_cairo_headers += $(cairo_win32_headers) -all_cairo_private += $(cairo_win32_private) -all_cairo_sources += $(cairo_win32_sources) -ifeq ($(CAIRO_HAS_WIN32_SURFACE),1) -enabled_cairo_headers += $(cairo_win32_headers) -enabled_cairo_private += $(cairo_win32_private) -enabled_cairo_sources += $(cairo_win32_sources) -endif -all_cairo_pkgconf += cairo-win32.pc -ifeq ($(CAIRO_HAS_WIN32_SURFACE),1) -enabled_cairo_pkgconf += cairo-win32.pc -endif - -supported_cairo_headers += $(cairo_win32_font_headers) -all_cairo_headers += $(cairo_win32_font_headers) -all_cairo_private += $(cairo_win32_font_private) -all_cairo_sources += $(cairo_win32_font_sources) -ifeq ($(CAIRO_HAS_WIN32_FONT),1) -enabled_cairo_headers += $(cairo_win32_font_headers) -enabled_cairo_private += $(cairo_win32_font_private) -enabled_cairo_sources += $(cairo_win32_font_sources) -endif -all_cairo_pkgconf += cairo-win32-font.pc -ifeq ($(CAIRO_HAS_WIN32_FONT),1) -enabled_cairo_pkgconf += cairo-win32-font.pc -endif - -supported_cairo_headers += $(cairo_png_headers) -all_cairo_headers += $(cairo_png_headers) -all_cairo_private += $(cairo_png_private) -all_cairo_sources += $(cairo_png_sources) -ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_png_headers) -enabled_cairo_private += $(cairo_png_private) -enabled_cairo_sources += $(cairo_png_sources) -endif -all_cairo_pkgconf += cairo-png.pc -ifeq ($(CAIRO_HAS_PNG_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-png.pc -endif - -unsupported_cairo_headers += $(cairo_gl_headers) -all_cairo_headers += $(cairo_gl_headers) -all_cairo_private += $(cairo_gl_private) -all_cairo_sources += $(cairo_gl_sources) -ifeq ($(CAIRO_HAS_GL_SURFACE),1) -enabled_cairo_headers += $(cairo_gl_headers) -enabled_cairo_private += $(cairo_gl_private) -enabled_cairo_sources += $(cairo_gl_sources) -endif -all_cairo_pkgconf += cairo-gl.pc -ifeq ($(CAIRO_HAS_GL_SURFACE),1) -enabled_cairo_pkgconf += cairo-gl.pc -endif - -unsupported_cairo_headers += $(cairo_glesv2_headers) -all_cairo_headers += $(cairo_glesv2_headers) -all_cairo_private += $(cairo_glesv2_private) -all_cairo_sources += $(cairo_glesv2_sources) -ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1) -enabled_cairo_headers += $(cairo_glesv2_headers) -enabled_cairo_private += $(cairo_glesv2_private) -enabled_cairo_sources += $(cairo_glesv2_sources) -endif -all_cairo_pkgconf += cairo-glesv2.pc -ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1) -enabled_cairo_pkgconf += cairo-glesv2.pc -endif - -unsupported_cairo_headers += $(cairo_glesv3_headers) -all_cairo_headers += $(cairo_glesv3_headers) -all_cairo_private += $(cairo_glesv3_private) -all_cairo_sources += $(cairo_glesv3_sources) -ifeq ($(CAIRO_HAS_GLESV3_SURFACE),1) -enabled_cairo_headers += $(cairo_glesv3_headers) -enabled_cairo_private += $(cairo_glesv3_private) -enabled_cairo_sources += $(cairo_glesv3_sources) -endif -all_cairo_pkgconf += cairo-glesv3.pc -ifeq ($(CAIRO_HAS_GLESV3_SURFACE),1) -enabled_cairo_pkgconf += cairo-glesv3.pc -endif - -supported_cairo_headers += $(cairo_egl_headers) -all_cairo_headers += $(cairo_egl_headers) -all_cairo_private += $(cairo_egl_private) -all_cairo_sources += $(cairo_egl_sources) -ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_egl_headers) -enabled_cairo_private += $(cairo_egl_private) -enabled_cairo_sources += $(cairo_egl_sources) -endif -all_cairo_pkgconf += cairo-egl.pc -ifeq ($(CAIRO_HAS_EGL_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-egl.pc -endif - -supported_cairo_headers += $(cairo_glx_headers) -all_cairo_headers += $(cairo_glx_headers) -all_cairo_private += $(cairo_glx_private) -all_cairo_sources += $(cairo_glx_sources) -ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_glx_headers) -enabled_cairo_private += $(cairo_glx_private) -enabled_cairo_sources += $(cairo_glx_sources) -endif -all_cairo_pkgconf += cairo-glx.pc -ifeq ($(CAIRO_HAS_GLX_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-glx.pc -endif - -supported_cairo_headers += $(cairo_wgl_headers) -all_cairo_headers += $(cairo_wgl_headers) -all_cairo_private += $(cairo_wgl_private) -all_cairo_sources += $(cairo_wgl_sources) -ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_wgl_headers) -enabled_cairo_private += $(cairo_wgl_private) -enabled_cairo_sources += $(cairo_wgl_sources) -endif -all_cairo_pkgconf += cairo-wgl.pc -ifeq ($(CAIRO_HAS_WGL_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-wgl.pc -endif - -supported_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) -all_cairo_sources += $(cairo_ft_sources) -ifeq ($(CAIRO_HAS_FT_FONT),1) -enabled_cairo_headers += $(cairo_ft_headers) -enabled_cairo_private += $(cairo_ft_private) -enabled_cairo_sources += $(cairo_ft_sources) -endif -all_cairo_pkgconf += cairo-ft.pc -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) -all_cairo_sources += $(cairo_ps_sources) -ifeq ($(CAIRO_HAS_PS_SURFACE),1) -enabled_cairo_headers += $(cairo_ps_headers) -enabled_cairo_private += $(cairo_ps_private) -enabled_cairo_sources += $(cairo_ps_sources) -endif -all_cairo_pkgconf += cairo-ps.pc -ifeq ($(CAIRO_HAS_PS_SURFACE),1) -enabled_cairo_pkgconf += cairo-ps.pc -endif - -supported_cairo_headers += $(cairo_pdf_headers) -all_cairo_headers += $(cairo_pdf_headers) -all_cairo_private += $(cairo_pdf_private) -all_cairo_sources += $(cairo_pdf_sources) -ifeq ($(CAIRO_HAS_PDF_SURFACE),1) -enabled_cairo_headers += $(cairo_pdf_headers) -enabled_cairo_private += $(cairo_pdf_private) -enabled_cairo_sources += $(cairo_pdf_sources) -endif -all_cairo_pkgconf += cairo-pdf.pc -ifeq ($(CAIRO_HAS_PDF_SURFACE),1) -enabled_cairo_pkgconf += cairo-pdf.pc -endif - -supported_cairo_headers += $(cairo_svg_headers) -all_cairo_headers += $(cairo_svg_headers) -all_cairo_private += $(cairo_svg_private) -all_cairo_sources += $(cairo_svg_sources) -ifeq ($(CAIRO_HAS_SVG_SURFACE),1) -enabled_cairo_headers += $(cairo_svg_headers) -enabled_cairo_private += $(cairo_svg_private) -enabled_cairo_sources += $(cairo_svg_sources) -endif -all_cairo_pkgconf += cairo-svg.pc -ifeq ($(CAIRO_HAS_SVG_SURFACE),1) -enabled_cairo_pkgconf += cairo-svg.pc -endif - -all_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) -all_cairo_sources += $(cairo_test_surfaces_sources) -ifeq ($(CAIRO_HAS_TEST_SURFACES),1) -enabled_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) -enabled_cairo_sources += $(cairo_test_surfaces_sources) -endif - -supported_cairo_headers += $(cairo_image_headers) -all_cairo_headers += $(cairo_image_headers) -all_cairo_private += $(cairo_image_private) -all_cairo_sources += $(cairo_image_sources) -enabled_cairo_headers += $(cairo_image_headers) -enabled_cairo_private += $(cairo_image_private) -enabled_cairo_sources += $(cairo_image_sources) - -supported_cairo_headers += $(cairo_mime_headers) -all_cairo_headers += $(cairo_mime_headers) -all_cairo_private += $(cairo_mime_private) -all_cairo_sources += $(cairo_mime_sources) -enabled_cairo_headers += $(cairo_mime_headers) -enabled_cairo_private += $(cairo_mime_private) -enabled_cairo_sources += $(cairo_mime_sources) - -supported_cairo_headers += $(cairo_recording_headers) -all_cairo_headers += $(cairo_recording_headers) -all_cairo_private += $(cairo_recording_private) -all_cairo_sources += $(cairo_recording_sources) -enabled_cairo_headers += $(cairo_recording_headers) -enabled_cairo_private += $(cairo_recording_private) -enabled_cairo_sources += $(cairo_recording_sources) - -supported_cairo_headers += $(cairo_observer_headers) -all_cairo_headers += $(cairo_observer_headers) -all_cairo_private += $(cairo_observer_private) -all_cairo_sources += $(cairo_observer_sources) -enabled_cairo_headers += $(cairo_observer_headers) -enabled_cairo_private += $(cairo_observer_private) -enabled_cairo_sources += $(cairo_observer_sources) - -unsupported_cairo_headers += $(cairo_tee_headers) -all_cairo_headers += $(cairo_tee_headers) -all_cairo_private += $(cairo_tee_private) -all_cairo_sources += $(cairo_tee_sources) -ifeq ($(CAIRO_HAS_TEE_SURFACE),1) -enabled_cairo_headers += $(cairo_tee_headers) -enabled_cairo_private += $(cairo_tee_private) -enabled_cairo_sources += $(cairo_tee_sources) -endif -all_cairo_pkgconf += cairo-tee.pc -ifeq ($(CAIRO_HAS_TEE_SURFACE),1) -enabled_cairo_pkgconf += cairo-tee.pc -endif - -unsupported_cairo_headers += $(cairo_xml_headers) -all_cairo_headers += $(cairo_xml_headers) -all_cairo_private += $(cairo_xml_private) -all_cairo_sources += $(cairo_xml_sources) -ifeq ($(CAIRO_HAS_XML_SURFACE),1) -enabled_cairo_headers += $(cairo_xml_headers) -enabled_cairo_private += $(cairo_xml_private) -enabled_cairo_sources += $(cairo_xml_sources) -endif -all_cairo_pkgconf += cairo-xml.pc -ifeq ($(CAIRO_HAS_XML_SURFACE),1) -enabled_cairo_pkgconf += cairo-xml.pc -endif - -supported_cairo_headers += $(cairo_user_headers) -all_cairo_headers += $(cairo_user_headers) -all_cairo_private += $(cairo_user_private) -all_cairo_sources += $(cairo_user_sources) -enabled_cairo_headers += $(cairo_user_headers) -enabled_cairo_private += $(cairo_user_private) -enabled_cairo_sources += $(cairo_user_sources) - -all_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers) -all_cairo_sources += $(cairo_pthread_sources) -ifeq ($(CAIRO_HAS_PTHREAD),1) -enabled_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers) -enabled_cairo_sources += $(cairo_pthread_sources) -endif - -supported_cairo_headers += $(cairo_gobject_headers) -all_cairo_headers += $(cairo_gobject_headers) -all_cairo_private += $(cairo_gobject_private) -all_cairo_sources += $(cairo_gobject_sources) -ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1) -enabled_cairo_headers += $(cairo_gobject_headers) -enabled_cairo_private += $(cairo_gobject_private) -enabled_cairo_sources += $(cairo_gobject_sources) -endif -all_cairo_pkgconf += cairo-gobject.pc -ifeq ($(CAIRO_HAS_GOBJECT_FUNCTIONS),1) -enabled_cairo_pkgconf += cairo-gobject.pc -endif - -all_cairo_private += $(cairo_trace_private) $(cairo_trace_headers) -all_cairo_sources += $(cairo_trace_sources) -ifeq ($(CAIRO_HAS_TRACE),1) -enabled_cairo_private += $(cairo_trace_private) $(cairo_trace_headers) -enabled_cairo_sources += $(cairo_trace_sources) -endif - -all_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers) -all_cairo_sources += $(cairo_interpreter_sources) -ifeq ($(CAIRO_HAS_INTERPRETER),1) -enabled_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers) -enabled_cairo_sources += $(cairo_interpreter_sources) -endif - -all_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) -all_cairo_sources += $(cairo_symbol_lookup_sources) -ifeq ($(CAIRO_HAS_SYMBOL_LOOKUP),1) -enabled_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) -enabled_cairo_sources += $(cairo_symbol_lookup_sources) -endif diff --git a/src/cairo-analysis-surface-private.h b/src/cairo-analysis-surface-private.h index 1e054c209..6489cceb8 100644 --- a/src/cairo-analysis-surface-private.h +++ b/src/cairo-analysis-surface-private.h @@ -38,7 +38,8 @@ #include "cairoint.h" cairo_private cairo_surface_t * -_cairo_analysis_surface_create (cairo_surface_t *target); +_cairo_analysis_surface_create (cairo_surface_t *target, + cairo_bool_t create_region_ids); cairo_private void _cairo_analysis_surface_set_ctm (cairo_surface_t *surface, @@ -64,6 +65,12 @@ cairo_private void _cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface, cairo_box_t *bbox); +cairo_private unsigned int +_cairo_analysis_surface_get_source_region_id (cairo_surface_t *surface); + +cairo_private unsigned int +_cairo_analysis_surface_get_mask_region_id (cairo_surface_t *surface); + cairo_private cairo_int_status_t _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, cairo_int_status_t status_b); @@ -71,4 +78,10 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, cairo_private cairo_surface_t * _cairo_null_surface_create (cairo_content_t content); +static inline cairo_bool_t +_cairo_surface_is_analysis (const cairo_surface_t *surface) +{ + return (cairo_internal_surface_type_t)surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS; +} + #endif /* CAIRO_ANALYSIS_SURFACE_H */ diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index a118e338c..0e7ba8a38 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* * Copyright © 2006 Keith Packard * Copyright © 2007 Adrian Johnson @@ -59,6 +60,10 @@ typedef struct { cairo_region_t fallback_region; cairo_box_t page_bbox; + cairo_bool_t create_region_ids; + unsigned source_region_id; + unsigned mask_region_id; + cairo_bool_t has_ctm; cairo_matrix_t ctm; @@ -257,7 +262,8 @@ _add_operation (cairo_analysis_surface_t *surface, static cairo_int_status_t _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents) + cairo_rectangle_int_t *extents, + unsigned int *regions_id) { const cairo_surface_pattern_t *surface_pattern; cairo_analysis_surface_t *tmp; @@ -280,7 +286,7 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, } tmp = (cairo_analysis_surface_t *) - _cairo_analysis_surface_create (surface->target); + _cairo_analysis_surface_create (surface->target, surface->create_region_ids); if (unlikely (tmp->base.status)) { status =tmp->base.status; goto cleanup1; @@ -295,13 +301,29 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, source = _cairo_surface_get_source (source, NULL); surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT - || pattern->extend == CAIRO_EXTEND_REFLECT); - status = _cairo_recording_surface_replay_and_create_regions (source, - &pattern->matrix, - &tmp->base, - surface_is_unbounded); - if (unlikely (status)) - goto cleanup2; + || pattern->extend == CAIRO_EXTEND_REFLECT); + + if (surface->create_region_ids) { + status = _cairo_recording_surface_region_array_attach (source, regions_id); + if (unlikely (status)) + goto cleanup2; + + status = _cairo_recording_surface_replay_and_create_regions (source, + *regions_id, + &pattern->matrix, + &tmp->base, + surface_is_unbounded); + if (unlikely (status)) + goto cleanup2; + } else { + status = _cairo_recording_surface_replay_with_clip (source, + &pattern->matrix, + &tmp->base, + NULL, /* target clip */ + surface_is_unbounded); + if (unlikely (status)) + goto cleanup2; + } /* black background or mime data fills entire extents */ if (!(source->content & CAIRO_CONTENT_ALPHA) || _cairo_surface_has_mime_image (source)) { @@ -412,6 +434,8 @@ _cairo_analysis_surface_paint (void *abstract_surface, cairo_int_status_t backend_status; cairo_rectangle_int_t extents; + surface->source_region_id = 0; + surface->mask_region_id = 0; if (surface->target->backend->paint == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { @@ -427,7 +451,10 @@ _cairo_analysis_surface_paint (void *abstract_surface, &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_rectangle_int_t rec_extents; - backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + backend_status = _analyze_recording_surface_pattern (surface, + source, + &rec_extents, + &surface->source_region_id); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -445,6 +472,8 @@ _cairo_analysis_surface_mask (void *abstract_surface, cairo_int_status_t backend_status; cairo_rectangle_int_t extents; + surface->source_region_id = 0; + surface->mask_region_id = 0; if (surface->target->backend->mask == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { @@ -468,7 +497,10 @@ _cairo_analysis_surface_mask (void *abstract_surface, src_surface = _cairo_surface_get_source (src_surface, NULL); if (_cairo_surface_is_recording (src_surface)) { backend_source_status = - _analyze_recording_surface_pattern (surface, source, &rec_extents); + _analyze_recording_surface_pattern (surface, + source, + &rec_extents, + &surface->source_region_id); if (_cairo_int_status_is_error (backend_source_status)) return backend_source_status; @@ -481,7 +513,10 @@ _cairo_analysis_surface_mask (void *abstract_surface, mask_surface = _cairo_surface_get_source (mask_surface, NULL); if (_cairo_surface_is_recording (mask_surface)) { backend_mask_status = - _analyze_recording_surface_pattern (surface, mask, &rec_extents); + _analyze_recording_surface_pattern (surface, + mask, + &rec_extents, + &surface->mask_region_id); if (_cairo_int_status_is_error (backend_mask_status)) return backend_mask_status; @@ -520,6 +555,8 @@ _cairo_analysis_surface_stroke (void *abstract_surface, cairo_int_status_t backend_status; cairo_rectangle_int_t extents; + surface->source_region_id = 0; + surface->mask_region_id = 0; if (surface->target->backend->stroke == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { @@ -538,7 +575,10 @@ _cairo_analysis_surface_stroke (void *abstract_surface, &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_rectangle_int_t rec_extents; - backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + backend_status = _analyze_recording_surface_pattern (surface, + source, + &rec_extents, + &surface->source_region_id); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -590,7 +630,10 @@ _cairo_analysis_surface_fill (void *abstract_surface, &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_rectangle_int_t rec_extents; - backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + backend_status = _analyze_recording_surface_pattern (surface, + source, + &rec_extents, + &surface->source_region_id); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -619,6 +662,9 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; + surface->source_region_id = 0; + surface->mask_region_id = 0; + /* Adapted from _cairo_surface_show_glyphs */ if (surface->target->backend->show_glyphs != NULL) { backend_status = @@ -654,7 +700,10 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_rectangle_int_t rec_extents; - backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + backend_status = _analyze_recording_surface_pattern (surface, + source, + &rec_extents, + &surface->source_region_id); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -699,6 +748,9 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; + surface->source_region_id = 0; + surface->mask_region_id = 0; + /* Adapted from _cairo_surface_show_glyphs */ backend_status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->target->backend->show_text_glyphs != NULL) { @@ -732,7 +784,10 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_rectangle_int_t rec_extents; - backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents); + _analyze_recording_surface_pattern (surface, + source, + &rec_extents, + &surface->source_region_id); _cairo_rectangle_intersect (&extents, &rec_extents); } @@ -760,6 +815,8 @@ _cairo_analysis_surface_tag (void *abstract_surface, cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; + surface->source_region_id = 0; + surface->mask_region_id = 0; backend_status = CAIRO_INT_STATUS_SUCCESS; if (surface->target->backend->tag != NULL) { backend_status = @@ -774,6 +831,14 @@ _cairo_analysis_surface_tag (void *abstract_surface, return backend_status; } +static cairo_bool_t +_cairo_analysis_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + return TRUE; +} + static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, @@ -808,11 +873,13 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { _cairo_analysis_surface_has_show_text_glyphs, _cairo_analysis_surface_show_text_glyphs, NULL, /* get_supported_mime_types */ - _cairo_analysis_surface_tag + _cairo_analysis_surface_tag, + _cairo_analysis_surface_supports_color_glyph }; cairo_surface_t * -_cairo_analysis_surface_create (cairo_surface_t *target) +_cairo_analysis_surface_create (cairo_surface_t *target, + cairo_bool_t create_region_ids) { cairo_analysis_surface_t *surface; cairo_status_t status; @@ -841,6 +908,10 @@ _cairo_analysis_surface_create (cairo_surface_t *target) surface->has_supported = FALSE; surface->has_unsupported = FALSE; + surface->create_region_ids = create_region_ids; + surface->source_region_id = 0; + surface->mask_region_id = 0; + _cairo_region_init (&surface->supported_region); _cairo_region_init (&surface->fallback_region); @@ -918,6 +989,23 @@ _cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface, *bbox = surface->page_bbox; } +unsigned int +_cairo_analysis_surface_get_source_region_id (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->source_region_id; +} + +unsigned int +_cairo_analysis_surface_get_mask_region_id (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->mask_region_id; +} + + /* null surface type: a surface that does nothing (has no side effects, yay!) */ static cairo_int_status_t @@ -926,6 +1014,12 @@ _paint_return_success (void *surface, const cairo_pattern_t *source, const cairo_clip_t *clip) { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -936,6 +1030,18 @@ _mask_return_success (void *surface, const cairo_pattern_t *mask, const cairo_clip_t *clip) { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -951,6 +1057,12 @@ _stroke_return_success (void *surface, cairo_antialias_t antialias, const cairo_clip_t *clip) { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -964,6 +1076,12 @@ _fill_return_success (void *surface, cairo_antialias_t antialias, const cairo_clip_t *clip) { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -976,6 +1094,12 @@ _show_glyphs_return_success (void *surface, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source; + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + return CAIRO_INT_STATUS_SUCCESS; } diff --git a/src/cairo-array.c b/src/cairo-array.c index db7b6de7a..1c91b7c73 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -143,7 +143,6 @@ _cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) /** * _cairo_array_index: * @array: a #cairo_array_t - * Returns: A pointer to the object stored at @index. * * If the resulting value is assigned to a pointer to an object of the same * element_size as initially passed to _cairo_array_init() then that @@ -161,6 +160,8 @@ _cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) * for (i = 0; i < _cairo_array_num_elements (&array); i++) * ... use values[i] here ... * </programlisting></informalexample> + * + * Returns: A pointer to the object stored at @index. **/ void * _cairo_array_index (cairo_array_t *array, unsigned int index) @@ -187,7 +188,6 @@ _cairo_array_index (cairo_array_t *array, unsigned int index) /** * _cairo_array_index_const: * @array: a #cairo_array_t - * Returns: A pointer to the object stored at @index. * * If the resulting value is assigned to a pointer to an object of the same * element_size as initially passed to _cairo_array_init() then that @@ -205,6 +205,8 @@ _cairo_array_index (cairo_array_t *array, unsigned int index) * for (i = 0; i < _cairo_array_num_elements (&array); i++) * ... read values[i] here ... * </programlisting></informalexample> + * + * Returns: A pointer to the object stored at @index. **/ const void * _cairo_array_index_const (const cairo_array_t *array, unsigned int index) @@ -330,9 +332,10 @@ _cairo_array_allocate (cairo_array_t *array, /** * _cairo_array_num_elements: * @array: a #cairo_array_t - * Returns: The number of elements stored in @array. * * This space was left intentionally blank, but gtk-doc filled it. + * + * Returns: The number of elements stored in @array. **/ unsigned int _cairo_array_num_elements (const cairo_array_t *array) @@ -343,10 +346,11 @@ _cairo_array_num_elements (const cairo_array_t *array) /** * _cairo_array_size: * @array: a #cairo_array_t - * Returns: The number of elements for which there is currently space - * allocated in @array. * * This space was left intentionally blank, but gtk-doc filled it. + * + * Returns: The number of elements for which there is currently space + * allocated in @array. **/ unsigned int _cairo_array_size (const cairo_array_t *array) diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h index a9eb38a7f..727f97c75 100644 --- a/src/cairo-atomic-private.h +++ b/src/cairo-atomic-private.h @@ -340,6 +340,8 @@ _cairo_atomic_ptr_cmpxchg_return_old (void **x, void *oldv, void *newv) #ifndef HAS_ATOMIC_OPS +typedef int cairo_atomic_int_t; + #if SIZEOF_VOID_P==SIZEOF_INT typedef unsigned int cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG @@ -350,8 +352,6 @@ typedef unsigned long long cairo_atomic_intptr_t; #error No matching integer pointer type #endif -typedef cairo_atomic_intptr_t cairo_atomic_int_t; - cairo_private void _cairo_atomic_int_inc (cairo_atomic_int_t *x); @@ -376,7 +376,8 @@ cairo_private cairo_atomic_int_t _cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x); void _cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val); -# define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x) +cairo_private void* +_cairo_atomic_ptr_get(void **x); #else # define _cairo_atomic_int_get(x) (*x) # define _cairo_atomic_int_get_relaxed(x) (*x) @@ -441,6 +442,7 @@ _cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv) #define _cairo_status_set_error(status, err) do { \ int ret__; \ assert (err < CAIRO_STATUS_LAST_STATUS); \ + assert (sizeof(*status) == sizeof(cairo_atomic_int_t)); \ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ * an unsigned integer instead, and about ignoring the return value. */ \ ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ diff --git a/src/cairo-atomic.c b/src/cairo-atomic.c index 2af50cd38..3c4d51972 100644 --- a/src/cairo-atomic.c +++ b/src/cairo-atomic.c @@ -42,7 +42,7 @@ COMPILE_TIME_ASSERT(sizeof(void*) == sizeof(int) || sizeof(void*) == sizeof(long long)); #else void -_cairo_atomic_int_inc (cairo_atomic_intptr_t *x) +_cairo_atomic_int_inc (cairo_atomic_int_t *x) { CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); *x += 1; @@ -50,7 +50,7 @@ _cairo_atomic_int_inc (cairo_atomic_intptr_t *x) } cairo_bool_t -_cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x) +_cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x) { cairo_bool_t ret; @@ -61,10 +61,10 @@ _cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x) return ret; } -cairo_atomic_intptr_t -_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv) +cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv) { - cairo_atomic_intptr_t ret; + cairo_atomic_int_t ret; CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); ret = *x; @@ -90,10 +90,10 @@ _cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv) } #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER -cairo_atomic_intptr_t -_cairo_atomic_int_get (cairo_atomic_intptr_t *x) +cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x) { - cairo_atomic_intptr_t ret; + cairo_atomic_int_t ret; CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); ret = *x; @@ -102,19 +102,31 @@ _cairo_atomic_int_get (cairo_atomic_intptr_t *x) return ret; } -cairo_atomic_intptr_t -_cairo_atomic_int_get_relaxed (cairo_atomic_intptr_t *x) +cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) { return _cairo_atomic_int_get (x); } void -_cairo_atomic_int_set_relaxed (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t val) +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) { CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); *x = val; CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); } + +void* +_cairo_atomic_ptr_get (void **x) +{ + void *ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} #endif #endif diff --git a/src/cairo-boxes.c b/src/cairo-boxes.c index 3c5d2a750..383a3eefb 100644 --- a/src/cairo-boxes.c +++ b/src/cairo-boxes.c @@ -102,16 +102,16 @@ _cairo_boxes_init_for_array (cairo_boxes_t *boxes, boxes->is_pixel_aligned = n == num_boxes; } -/** _cairo_boxes_limit: +/** + * _cairo_boxes_limit: + * @boxes: the box set to be filled (return buffer) + * @limits: array of the limiting boxes to compute the bounding + * box from + * @num_limits: length of the limits array * * Computes the minimum bounding box of the given list of boxes and assign * it to the given boxes set. It also assigns that list as the list of * limiting boxes in the box set. - * - * @param boxes the box set to be filled (return buffer) - * @param limits array of the limiting boxes to compute the bounding - * box from - * @param num_limits length of the limits array */ void _cairo_boxes_limit (cairo_boxes_t *boxes, @@ -276,13 +276,13 @@ _cairo_boxes_add (cairo_boxes_t *boxes, return boxes->status; } -/** _cairo_boxes_extents: +/** + * _cairo_boxes_extents: + * @boxes: The box set whose minimum bounding is computed. + * @box: Return buffer for the computed result. * * Computes the minimum bounding box of the given box set and stores * it in the given box. - * - * @param boxes The box set whose minimum bounding is computed. - * @param box Return buffer for the computed result. */ void _cairo_boxes_extents (const cairo_boxes_t *boxes, @@ -336,15 +336,16 @@ _cairo_boxes_clear (cairo_boxes_t *boxes) boxes->is_pixel_aligned = TRUE; } -/** _cairo_boxes_to_array: +/** + * _cairo_boxes_to_array: + * @boxes The box set to be converted. + * @num_boxes Return buffer for the number of boxes (array count). * * Linearize a box set of possibly multiple chunks into one big chunk * and returns an array of boxes * - * @param boxes The box set to be converted. - * @param num_boxes Return buffer for the number of boxes (array count). - * @return Pointer to the newly allocated array of boxes - * (the number o elements is given in num_boxes). + * Return value: Pointer to the newly allocated array of boxes (the number o + * elements is given in num_boxes). */ cairo_box_t * _cairo_boxes_to_array (const cairo_boxes_t *boxes, diff --git a/src/cairo-cache.c b/src/cairo-cache.c index 96809b585..afdca984e 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -56,7 +56,6 @@ _cairo_cache_entry_is_non_zero (const void *entry) * @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 - * Returns: the newly created #cairo_cache_t * * Creates a new cache using the keys_equal() function to determine * the equality of entries. @@ -84,6 +83,8 @@ _cairo_cache_entry_is_non_zero (const void *entry) * _cairo_cache_freeze() and _cairo_cache_thaw() calls can be * used to establish a window during which no automatic removal of * entries will occur. + * + * Returns: the newly created #cairo_cache_t **/ cairo_status_t _cairo_cache_init (cairo_cache_t *cache, @@ -336,3 +337,10 @@ _cairo_hash_bytes (uintptr_t hash, hash = ((hash << 5) + hash) + *bytes++; return hash; } + +uintptr_t +_cairo_hash_uintptr (uintptr_t hash, + uintptr_t u) +{ + return _cairo_hash_bytes (hash, &u, sizeof(u)); +} diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c index 4bf22e2b7..c7aaec4a8 100644 --- a/src/cairo-cff-subset.c +++ b/src/cairo-cff-subset.c @@ -390,7 +390,7 @@ encode_index_offset (unsigned char *p, int offset_size, unsigned long offset) return p + offset_size; } -static unsigned long +static size_t decode_index_offset(unsigned char *p, int off_size) { unsigned long offset = 0; @@ -412,8 +412,8 @@ cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_pt cff_index_element_t element; unsigned char *data, *p; cairo_status_t status; - int offset_size, count, start, i; - int end = 0; + int offset_size, count, i; + size_t start, end = 0; p = *ptr; if (p + 2 > end_ptr) @@ -422,7 +422,7 @@ cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_pt p += 2; if (count > 0) { offset_size = *p++; - if (p + (count + 1)*offset_size > end_ptr) + if (p + (count + 1)*offset_size > end_ptr || offset_size > 4) return CAIRO_INT_STATUS_UNSUPPORTED; data = p + offset_size*(count + 1) - 1; start = decode_index_offset (p, offset_size); @@ -430,7 +430,7 @@ cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_pt for (i = 0; i < count; i++) { end = decode_index_offset (p, offset_size); p += offset_size; - if (p > end_ptr) + if (p > end_ptr || end < start || data + end > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; element.length = end - start; element.is_copy = FALSE; @@ -875,7 +875,7 @@ cairo_cff_font_read_name (cairo_cff_font_t *font) cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); - if (!font->is_opentype) { + if (status == CAIRO_INT_STATUS_SUCCESS && !font->is_opentype) { element = _cairo_array_index (&index, 0); p = element->data; len = element->length; @@ -890,12 +890,10 @@ cairo_cff_font_read_name (cairo_cff_font_t *font) len -= 7; } } - font->ps_name = _cairo_malloc (len + 1); - if (unlikely (font->ps_name == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - memcpy (font->ps_name, p, len); - font->ps_name[len] = 0; + font->ps_name = _cairo_strndup ((char*)p, len); + if (unlikely (font->ps_name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_escape_ps_name (&font->ps_name); } diff --git a/src/cairo-colr-glyph-render.c b/src/cairo-colr-glyph-render.c new file mode 100644 index 000000000..28254fd51 --- /dev/null +++ b/src/cairo-colr-glyph-render.c @@ -0,0 +1,1248 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2022 Matthias Clasen + * + * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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. + * + * Contributor(s): + * Matthias Clasen <mclasen@redhat.com> + */ + +#include "cairoint.h" +#include "cairo-array-private.h" +#include "cairo-ft-private.h" +#include "cairo-path-private.h" +#include "cairo-pattern-private.h" + +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <string.h> + +#if HAVE_FT_COLR_V1 + +#include <ft2build.h> +#include FT_CONFIG_OPTIONS_H +#include FT_COLOR_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_SIZES_H + +/* #define DEBUG_COLR 1 */ + +typedef struct _cairo_colr_glyph_render { + FT_Face face; + FT_Color *palette; + unsigned int num_palette_entries; + cairo_pattern_t *foreground_marker; + cairo_pattern_t *foreground_source; + cairo_bool_t foreground_source_used; + int level; +} cairo_colr_glyph_render_t; + +static cairo_status_t +draw_paint (cairo_colr_glyph_render_t *render, + FT_OpaquePaint *paint, + cairo_t *cr); + + +static inline double +double_from_16_16 (FT_Fixed f) +{ + return f / (double) (1 << 16); +} + +static inline double +double_from_26_6 (FT_F26Dot6 f) +{ + return f / (double) (1 << 6); +} + +static inline double +double_from_2_14 (FT_F2Dot14 f) +{ + return f / (double) (1 << 14); +} + +static inline double +interpolate (double f0, double f1, double f) +{ + return f0 + f * (f1 - f0); +} + +static inline void +interpolate_points (cairo_point_double_t *p0, + cairo_point_double_t *p1, + double f, + cairo_point_double_t *out) +{ + out->x = interpolate (p0->x, p1->x, f); + out->y = interpolate (p0->y, p1->y, f); +} + +static inline void +interpolate_colors (cairo_color_t *c0, + cairo_color_t *c1, + double f, + cairo_color_t *out) +{ + out->red = interpolate (c0->red, c1->red, f); + out->green = interpolate (c0->green, c1->green, f); + out->blue = interpolate (c0->blue, c1->blue, f); + out->alpha = interpolate (c0->alpha, c1->alpha, f); +} + +static inline double +dot (cairo_point_double_t p, cairo_point_double_t q) +{ + return p.x * q.x + p.y * q.y; +} + +static inline cairo_point_double_t +normalize (cairo_point_double_t p) +{ + double len = sqrt (dot (p, p)); + + return (cairo_point_double_t) { p.x / len, p.y / len }; +} + +static inline cairo_point_double_t +sum (cairo_point_double_t p, cairo_point_double_t q) +{ + return (cairo_point_double_t) { p.x + q.x, p.y + q.y }; +} + +static inline cairo_point_double_t +difference (cairo_point_double_t p, cairo_point_double_t q) +{ + return (cairo_point_double_t) { p.x - q.x, p.y - q.y }; +} + +static inline cairo_point_double_t +scale (cairo_point_double_t p, double f) +{ + return (cairo_point_double_t) { p.x * f, p.y * f }; +} + +static cairo_operator_t +cairo_operator_from_ft_composite_mode (FT_Composite_Mode mode) +{ + switch (mode) + { + case FT_COLR_COMPOSITE_CLEAR: return CAIRO_OPERATOR_CLEAR; + case FT_COLR_COMPOSITE_SRC: return CAIRO_OPERATOR_SOURCE; + case FT_COLR_COMPOSITE_DEST: return CAIRO_OPERATOR_DEST; + case FT_COLR_COMPOSITE_SRC_OVER: return CAIRO_OPERATOR_OVER; + case FT_COLR_COMPOSITE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER; + case FT_COLR_COMPOSITE_SRC_IN: return CAIRO_OPERATOR_IN; + case FT_COLR_COMPOSITE_DEST_IN: return CAIRO_OPERATOR_DEST_IN; + case FT_COLR_COMPOSITE_SRC_OUT: return CAIRO_OPERATOR_OUT; + case FT_COLR_COMPOSITE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT; + case FT_COLR_COMPOSITE_SRC_ATOP: return CAIRO_OPERATOR_ATOP; + case FT_COLR_COMPOSITE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP; + case FT_COLR_COMPOSITE_XOR: return CAIRO_OPERATOR_XOR; + case FT_COLR_COMPOSITE_PLUS: return CAIRO_OPERATOR_ADD; + case FT_COLR_COMPOSITE_SCREEN: return CAIRO_OPERATOR_SCREEN; + case FT_COLR_COMPOSITE_OVERLAY: return CAIRO_OPERATOR_OVERLAY; + case FT_COLR_COMPOSITE_DARKEN: return CAIRO_OPERATOR_DARKEN; + case FT_COLR_COMPOSITE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN; + case FT_COLR_COMPOSITE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE; + case FT_COLR_COMPOSITE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN; + case FT_COLR_COMPOSITE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT; + case FT_COLR_COMPOSITE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT; + case FT_COLR_COMPOSITE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE; + case FT_COLR_COMPOSITE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION; + case FT_COLR_COMPOSITE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY; + case FT_COLR_COMPOSITE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE; + case FT_COLR_COMPOSITE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION; + case FT_COLR_COMPOSITE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR; + case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY; + case FT_COLR_COMPOSITE_MAX: + default: + ASSERT_NOT_REACHED; + } +} + +static cairo_extend_t +cairo_extend_from_ft_paint_extend (FT_PaintExtend extend) +{ + switch (extend) + { + case FT_COLR_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD; + case FT_COLR_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT; + case FT_COLR_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT; + default: + ASSERT_NOT_REACHED; + } +} + +static cairo_status_t +draw_paint_colr_layers (cairo_colr_glyph_render_t *render, + FT_PaintColrLayers *colr_layers, + cairo_t *cr) +{ + FT_OpaquePaint paint; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintColrLayers\n", 2 * render->level, ""); +#endif + + while (FT_Get_Paint_Layers (render->face, &colr_layers->layer_iterator, &paint)) { + cairo_push_group (cr); + status = draw_paint (render, &paint, cr); + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint (cr); + + if (unlikely (status)) + break; + } + + return status; +} + +static void +get_palette_color (cairo_colr_glyph_render_t *render, + FT_ColorIndex *ci, + cairo_color_t *color, + double *colr_alpha, + cairo_bool_t *is_foreground_color) +{ + cairo_bool_t foreground = FALSE; + + if (ci->palette_index == 0xffff || ci->palette_index >= render->num_palette_entries) { + color->red = 0; + color->green = 0; + color->blue = 0; + color->alpha = 1; + foreground = TRUE; + } else { + FT_Color c = render->palette[ci->palette_index]; + color->red = c.red / 255.0; + color->green = c.green / 255.0; + color->blue = c.blue / 255.0; + color->alpha = c.alpha / 255.0; + } + + *colr_alpha = double_from_2_14 (ci->alpha); + *is_foreground_color = foreground; +} + +static cairo_status_t +draw_paint_solid (cairo_colr_glyph_render_t *render, + FT_PaintSolid *solid, + cairo_t *cr) +{ + cairo_color_t color; + double colr_alpha; + cairo_bool_t is_foreground_color; + +#if DEBUG_COLR + printf ("%*sDraw PaintSolid\n", 2 * render->level, ""); +#endif + + get_palette_color (render, &solid->color, &color, &colr_alpha, &is_foreground_color); + if (is_foreground_color) { + cairo_set_source (cr, render->foreground_marker); + cairo_paint_with_alpha (cr, colr_alpha); + } else { + cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha * colr_alpha); + cairo_paint (cr); + } + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _cairo_colr_color_stop { + cairo_color_t color; + double position; +} cairo_colr_color_stop_t; + +typedef struct _cairo_colr_color_line { + int n_stops; + cairo_colr_color_stop_t *stops; +} cairo_colr_color_line_t; + +static void +free_colorline (cairo_colr_color_line_t *cl) +{ + free (cl->stops); + free (cl); +} + +static int +_compare_stops (const void *p1, const void *p2) +{ + const cairo_colr_color_stop_t *c1 = p1; + const cairo_colr_color_stop_t *c2 = p2; + + if (c1->position < c2->position) + return -1; + else if (c1->position > c2->position) + return 1; + else + return 0; +} + +static cairo_colr_color_line_t * +read_colorline (cairo_colr_glyph_render_t *render, + FT_ColorLine *colorline) +{ + cairo_colr_color_line_t *cl; + FT_ColorStop stop; + int i; + double colr_alpha; + cairo_bool_t is_foreground_color; + + cl = calloc (1, sizeof (cairo_colr_color_line_t)); + if (unlikely (cl == NULL)) + return NULL; + + cl->n_stops = colorline->color_stop_iterator.num_color_stops; + cl->stops = calloc (cl->n_stops, sizeof (cairo_colr_color_stop_t)); + if (unlikely (cl->stops == NULL)) { + free (cl); + return NULL; + } + + i = 0; + while (FT_Get_Colorline_Stops (render->face, &stop, &colorline->color_stop_iterator)) { + cl->stops[i].position = double_from_16_16 (stop.stop_offset); + get_palette_color (render, &stop.color, &cl->stops[i].color, &colr_alpha, &is_foreground_color); + if (is_foreground_color) { + double red, green, blue, alpha; + if (cairo_pattern_get_rgba (render->foreground_source, + &red, &green, &blue, &alpha) == CAIRO_STATUS_SUCCESS) + { + cl->stops[i].color.red = red; + cl->stops[i].color.green = green; + cl->stops[i].color.blue = blue; + cl->stops[i].color.alpha = alpha * colr_alpha; + render->foreground_source_used = TRUE; + } + else + { + cl->stops[i].color.red = 0; + cl->stops[i].color.green = 0; + cl->stops[i].color.blue = 0; + cl->stops[i].color.alpha = colr_alpha; + } + } else { + cl->stops[i].color.alpha *= colr_alpha; + } + i++; + } + + qsort (cl->stops, cl->n_stops, sizeof (cairo_colr_color_stop_t), _compare_stops); + + return cl; +} + +static void +reduce_anchors (FT_PaintLinearGradient *gradient, + cairo_point_double_t *pp0, + cairo_point_double_t *pp1) +{ + cairo_point_double_t p0, p1, p2; + cairo_point_double_t q1, q2; + double s; + double k; + + p0.x = double_from_16_16 (gradient->p0.x); + p0.y = double_from_16_16 (gradient->p0.y); + p1.x = double_from_16_16 (gradient->p1.x); + p1.y = double_from_16_16 (gradient->p1.y); + p2.x = double_from_16_16 (gradient->p2.x); + p2.y = double_from_16_16 (gradient->p2.y); + + q2.x = p2.x - p0.x; + q2.y = p2.y - p0.y; + q1.x = p1.x - p0.x; + q1.y = p1.y - p0.y; + + s = q2.x * q2.x + q2.y * q2.y; + if (s < 0.000001) + { + pp0->x = p0.x; pp0->y = p0.y; + pp1->x = p1.x; pp1->y = p1.y; + return; + } + + k = (q2.x * q1.x + q2.y * q1.y) / s; + pp0->x = p0.x; + pp0->y = p0.y; + pp1->x = p1.x - k * q2.x; + pp1->y = p1.y - k * q2.y; +} + +static void +normalize_colorline (cairo_colr_color_line_t *cl, + double *out_min, + double *out_max) +{ + double min, max; + + *out_min = 0.; + *out_max = 1.; + + min = max = cl->stops[0].position; + for (int i = 0; i < cl->n_stops; i++) { + cairo_colr_color_stop_t *stop = &cl->stops[i]; + min = MIN (min, stop->position); + max = MAX (max, stop->position); + } + + if (min != max) { + for (int i = 0; i < cl->n_stops; i++) { + cairo_colr_color_stop_t *stop = &cl->stops[i]; + stop->position = (stop->position - min) / (max - min); + } + *out_min = min; + *out_max = max; + } +} + +static cairo_status_t +draw_paint_linear_gradient (cairo_colr_glyph_render_t *render, + FT_PaintLinearGradient *gradient, + cairo_t *cr) +{ + cairo_colr_color_line_t *cl; + cairo_point_double_t p0, p1; + cairo_point_double_t pp0, pp1; + cairo_pattern_t *pattern; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + double min, max; + +#if DEBUG_COLR + printf ("%*sDraw PaintLinearGradient\n", 2 * render->level, ""); +#endif + + cl = read_colorline (render, &gradient->colorline); + if (unlikely (cl == NULL)) + return CAIRO_STATUS_NO_MEMORY; + + /* cairo only allows stop positions between 0 and 1 */ + normalize_colorline (cl, &min, &max); + reduce_anchors (gradient, &p0, &p1); + interpolate_points (&p0, &p1, min, &pp0); + interpolate_points (&p0, &p1, max, &pp1); + + pattern = cairo_pattern_create_linear (pp0.x, pp0.y, pp1.x, pp1.y); + + cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend)); + + for (int i = 0; i < cl->n_stops; i++) { + cairo_colr_color_stop_t *stop = &cl->stops[i]; + cairo_pattern_add_color_stop_rgba (pattern, stop->position, + stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + free_colorline (cl); + + return status; +} + +static cairo_status_t +draw_paint_radial_gradient (cairo_colr_glyph_render_t *render, + FT_PaintRadialGradient *gradient, + cairo_t *cr) +{ + cairo_colr_color_line_t *cl; + cairo_point_double_t start, end; + cairo_point_double_t start1, end1; + double start_radius, end_radius; + double start_radius1, end_radius1; + double min, max; + cairo_pattern_t *pattern; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintRadialGradient\n", 2 * render->level, ""); +#endif + + cl = read_colorline (render, &gradient->colorline); + if (unlikely (cl == NULL)) + return CAIRO_STATUS_NO_MEMORY; + + start.x = double_from_16_16 (gradient->c0.x); + start.y = double_from_16_16 (gradient->c0.y); + end.x = double_from_16_16 (gradient->c1.x); + end.y = double_from_16_16 (gradient->c1.y); + + start_radius = double_from_16_16 (gradient->r0); + end_radius = double_from_16_16 (gradient->r1); + + /* cairo only allows stop positions between 0 and 1 */ + normalize_colorline (cl, &min, &max); + interpolate_points (&start, &end, min, &start1); + interpolate_points (&start, &end, max, &end1); + start_radius1 = interpolate (start_radius, end_radius, min); + end_radius1 = interpolate (start_radius, end_radius, max); + + pattern = cairo_pattern_create_radial (start1.x, start1.y, start_radius1, + end1.x, end1.y, end_radius1); + + cairo_pattern_set_extend (pattern, cairo_extend_from_ft_paint_extend (gradient->colorline.extend)); + + for (int i = 0; i < cl->n_stops; i++) { + cairo_colr_color_stop_t *stop = &cl->stops[i]; + cairo_pattern_add_color_stop_rgba (pattern, stop->position, + stop->color.red, stop->color.green, stop->color.blue, stop->color.alpha); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + free_colorline (cl); + + return status; +} + +typedef struct { + cairo_point_double_t center, p0, c0, c1, p1; + cairo_color_t color0, color1; +} cairo_colr_gradient_patch_t; + +static void +add_patch (cairo_pattern_t *pattern, + cairo_point_double_t *center, + cairo_colr_gradient_patch_t *p) +{ + cairo_mesh_pattern_begin_patch (pattern); + cairo_mesh_pattern_move_to (pattern, center->x, center->y); + cairo_mesh_pattern_line_to (pattern, p->p0.x, p->p0.y); + cairo_mesh_pattern_curve_to (pattern, + p->c0.x, p->c0.y, + p->c1.x, p->c1.y, + p->p1.x, p->p1.y); + cairo_mesh_pattern_line_to (pattern, center->x, center->y); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, + p->color0.red, + p->color0.green, + p->color0.blue, + p->color0.alpha); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, + p->color0.red, + p->color0.green, + p->color0.blue, + p->color0.alpha); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, + p->color1.red, + p->color1.green, + p->color1.blue, + p->color1.alpha); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, + p->color1.red, + p->color1.green, + p->color1.blue, + p->color1.alpha); + cairo_mesh_pattern_end_patch (pattern); +} + +#define MAX_ANGLE (M_PI / 8.) + +static void +add_sweep_gradient_patches1 (cairo_point_double_t *center, + double radius, + double a0, + cairo_color_t *c0, + double a1, + cairo_color_t *c1, + cairo_pattern_t *pattern) +{ + + int num_splits; + cairo_point_double_t p0; + cairo_color_t color0, color1; + + num_splits = ceilf (fabs (a1 - a0) / MAX_ANGLE); + p0 = (cairo_point_double_t) { cosf (a0), sinf (a0) }; + color0 = *c0; + + for (int a = 0; a < num_splits; a++) { + double k = (a + 1.) / num_splits; + double angle1; + cairo_point_double_t p1; + cairo_point_double_t A, U; + cairo_point_double_t C0, C1; + cairo_colr_gradient_patch_t patch; + + angle1 = interpolate (a0, a1, k); + interpolate_colors (c0, c1, k, &color1); + + patch.color0 = color0; + patch.color1 = color1; + + p1 = (cairo_point_double_t) { cosf (angle1), sinf (angle1) }; + patch.p0 = sum (*center, scale (p0, radius)); + patch.p1 = sum (*center, scale (p1, radius)); + + A = normalize (sum (p0, p1)); + U = (cairo_point_double_t) { -A.y, A.x }; + C0 = sum (A, scale (U, dot (difference (p0, A), p0) / dot (U, p0))); + C1 = sum (A, scale (U, dot (difference (p1, A), p1) / dot (U, p1))); + patch.c0 = sum (*center, scale (sum (C0, scale (difference (C0, p0), 0.33333)), radius)); + patch.c1 = sum (*center, scale (sum (C1, scale (difference (C1, p1), 0.33333)), radius)); + + add_patch (pattern, center, &patch); + + p0 = p1; + color0 = color1; + } +} + +static void +add_sweep_gradient_patches (cairo_colr_color_line_t *cl, + cairo_extend_t extend, + cairo_point_double_t *center, + double radius, + double start_angle, + double end_angle, + cairo_pattern_t *pattern) +{ + double *angles; + cairo_color_t color0, color1; + + if (start_angle == end_angle) { + if (extend == CAIRO_EXTEND_PAD) { + if (start_angle > 0) + add_sweep_gradient_patches1 (center, radius, + 0., &cl->stops[0].color, + start_angle, &cl->stops[0].color, + pattern); + if (end_angle < 2 * M_PI) + add_sweep_gradient_patches1 (center, radius, + end_angle, &cl->stops[cl->n_stops - 1].color, + 2 * M_PI, &cl->stops[cl->n_stops - 1].color, + pattern); + } + return; + } + + assert (start_angle != end_angle); + + angles = alloca (sizeof (double) * cl->n_stops); + + for (int i = 0; i < cl->n_stops; i++) + angles[i] = start_angle + cl->stops[i].position * (end_angle - start_angle); + + /* handle directions */ + if (end_angle < start_angle) { + for (int i = 0; i < cl->n_stops - 1 - i; i++) { + cairo_colr_color_stop_t stop = cl->stops[i]; + double a = angles[i]; + cl->stops[i] = cl->stops[cl->n_stops - 1 - i]; + cl->stops[cl->n_stops - 1 - i] = stop; + angles[i] = angles[cl->n_stops - 1 - i]; + angles[cl->n_stops - 1 - i] = a; + } + } + + if (extend == CAIRO_EXTEND_PAD) + { + int pos; + + color0 = cl->stops[0].color; + for (pos = 0; pos < cl->n_stops; pos++) { + if (angles[pos] >= 0) { + if (pos > 0) { + double k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color0); + } + break; + } + } + if (pos == cl->n_stops) { + /* everything is below 0 */ + color0 = cl->stops[cl->n_stops - 1].color; + add_sweep_gradient_patches1 (center, radius, + 0., &color0, + 2 * M_PI, &color0, + pattern); + return; + } + + add_sweep_gradient_patches1 (center, radius, + 0., &color0, + angles[pos], &cl->stops[pos].color, + pattern); + + for (pos++; pos < cl->n_stops; pos++) { + if (angles[pos] <= 2 * M_PI) { + add_sweep_gradient_patches1 (center, radius, + angles[pos - 1], &cl->stops[pos - 1].color, + angles[pos], &cl->stops[pos].color, + pattern); + } else { + double k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + interpolate_colors (&cl->stops[pos - 1].color, &cl->stops[pos].color, k, &color1); + add_sweep_gradient_patches1 (center, radius, + angles[pos - 1], &cl->stops[pos - 1].color, + 2 * M_PI, &color1, + pattern); + break; + } + } + + if (pos == cl->n_stops) { + /* everything is below 2*M_PI */ + color0 = cl->stops[cl->n_stops - 1].color; + add_sweep_gradient_patches1 (center, radius, + angles[cl->n_stops - 1], &color0, + 2 * M_PI, &color0, + pattern); + return; + } + } else { + int k; + double span; + + span = angles[cl->n_stops - 1] - angles[0]; + k = 0; + if (angles[0] >= 0) { + double ss = angles[0]; + while (ss > 0) { + if (span > 0) { + ss -= span; + k--; + } else { + ss += span; + k++; + } + } + } + else if (angles[0] < 0) + { + double ee = angles[cl->n_stops - 1]; + while (ee < 0) { + if (span > 0) { + ee += span; + k++; + } else { + ee -= span; + k--; + } + } + } + + //assert (angles[0] + k * span <= 0 && 0 < angles[cl->n_stops - 1] + k * span); + + for (int l = k; TRUE; l++) { + for (int i = 1; i < cl->n_stops; i++) { + double a0, a1; + cairo_color_t *c0, *c1; + + if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) { + a0 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - (i-1)] + l * span; + a1 = angles[0] + angles[cl->n_stops - 1] - angles[cl->n_stops - 1 - i] + l * span; + c0 = &cl->stops[cl->n_stops - 1 - (i-1)].color; + c1 = &cl->stops[cl->n_stops - 1 - i].color; + } else { + a0 = angles[i-1] + l * span; + a1 = angles[i] + l * span; + c0 = &cl->stops[i-1].color; + c1 = &cl->stops[i].color; + } + + if (a1 < 0) + continue; + + if (a0 < 0) { + cairo_color_t color; + double f = (0 - a0)/(a1 - a0); + interpolate_colors (c0, c1, f, &color); + add_sweep_gradient_patches1 (center, radius, + 0, &color, + a1, c1, + pattern); + } else if (a1 >= 2 * M_PI) { + cairo_color_t color; + double f = (2 * M_PI - a0)/(a1 - a0); + interpolate_colors (c0, c1, f, &color); + add_sweep_gradient_patches1 (center, radius, + a0, c0, + 2 * M_PI, &color, + pattern); + return; + } else { + add_sweep_gradient_patches1 (center, radius, + a0, c0, + a1, c1, + pattern); + } + } + } + } +} + +static cairo_status_t +draw_paint_sweep_gradient (cairo_colr_glyph_render_t *render, + FT_PaintSweepGradient *gradient, + cairo_t *cr) +{ + cairo_colr_color_line_t *cl; + cairo_point_double_t center; + double start_angle, end_angle; + double x1, y1, x2, y2; + double max_x, max_y, R; + cairo_pattern_t *pattern; + cairo_extend_t extend; + +#if DEBUG_COLR + printf ("%*sDraw PaintSweepGradient\n", 2 * render->level, ""); +#endif + + cl = read_colorline (render, &gradient->colorline); + if (unlikely (cl == NULL)) + return CAIRO_STATUS_NO_MEMORY; + + center.x = double_from_16_16 (gradient->center.x); + center.y = double_from_16_16 (gradient->center.y); + start_angle = (double_from_16_16 (gradient->start_angle) + 1) * M_PI; + end_angle = (double_from_16_16 (gradient->end_angle) + 1) * M_PI; + + pattern = cairo_pattern_create_mesh (); + + cairo_clip_extents (cr, &x1, &y1, &x2, &y2); + max_x = MAX ((x1 - center.x) * (x1 - center.x), (x2 - center.x) * (x2 - center.x)); + max_y = MAX ((y1 - center.y) * (y1 - center.y), (y2 - center.y) * (y2 - center.y)); + R = sqrt (max_x + max_y); + + extend = cairo_extend_from_ft_paint_extend (gradient->colorline.extend); + + add_sweep_gradient_patches (cl, extend, ¢er, R, start_angle, end_angle, pattern); + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + free_colorline (cl); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +draw_paint_glyph (cairo_colr_glyph_render_t *render, + FT_PaintGlyph *glyph, + cairo_t *cr) +{ + cairo_path_fixed_t *path_fixed; + cairo_path_t *path; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + FT_Error error; + +#if DEBUG_COLR + printf ("%*sDraw PaintGlyph\n", 2 * render->level, ""); +#endif + + error = FT_Load_Glyph (render->face, glyph->glyphID, FT_LOAD_DEFAULT); + status = _cairo_ft_to_cairo_error (error); + if (unlikely (status)) + return status; + + status = _cairo_ft_face_decompose_glyph_outline (render->face, &path_fixed); + if (unlikely (status)) + return status; + + cairo_save (cr); + cairo_identity_matrix (cr); + path = _cairo_path_create (path_fixed, cr); + _cairo_path_fixed_destroy (path_fixed); + cairo_restore (cr); + + cairo_save (cr); + + cairo_new_path (cr); + cairo_append_path (cr, path); + cairo_path_destroy (path); + cairo_clip (cr); + + status = draw_paint (render, &glyph->paint, cr); + + cairo_restore (cr); + + return status; +} + +static cairo_status_t draw_colr_glyph (cairo_colr_glyph_render_t *render, + unsigned long glyph, + FT_Color_Root_Transform root, + cairo_t *cr); + +static cairo_status_t +draw_paint_colr_glyph (cairo_colr_glyph_render_t *render, + FT_PaintColrGlyph *colr_glyph, + cairo_t *cr) +{ +#if DEBUG_COLR + printf ("%*sDraw PaintColrGlyph\n", 2 * render->level, ""); +#endif + + return draw_colr_glyph (render, colr_glyph->glyphID, FT_COLOR_NO_ROOT_TRANSFORM, cr); +} + +static cairo_status_t +draw_paint_transform (cairo_colr_glyph_render_t *render, + FT_PaintTransform *transform, + cairo_t *cr) +{ + cairo_matrix_t t; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintTransform\n", 2 * render->level, ""); +#endif + + cairo_matrix_init (&t, + double_from_16_16 (transform->affine.xx), + double_from_16_16 (transform->affine.yx), + double_from_16_16 (transform->affine.xy), + double_from_16_16 (transform->affine.yy), + double_from_16_16 (transform->affine.dx), + double_from_16_16 (transform->affine.dy)); + + cairo_save (cr); + + cairo_transform (cr, &t); + status = draw_paint (render, &transform->paint, cr); + + cairo_restore (cr); + + return status; +} + +static cairo_status_t +draw_paint_translate (cairo_colr_glyph_render_t *render, + FT_PaintTranslate *translate, + cairo_t *cr) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintTranslate\n", 2 * render->level, ""); +#endif + + cairo_save (cr); + + cairo_translate (cr, double_from_16_16 (translate->dx), double_from_16_16 (translate->dy)); + status = draw_paint (render, &translate->paint, cr); + + cairo_restore (cr); + + return status; +} + +static cairo_status_t +draw_paint_rotate (cairo_colr_glyph_render_t *render, + FT_PaintRotate *rotate, + cairo_t *cr) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintRotate\n", 2 * render->level, ""); +#endif + + cairo_save (cr); + + cairo_translate (cr, double_from_16_16 (rotate->center_x), double_from_16_16 (rotate->center_y)); + cairo_rotate (cr, double_from_16_16 (rotate->angle) * M_PI); + cairo_translate (cr, - double_from_16_16 (rotate->center_x), - double_from_16_16 (rotate->center_y)); + status = draw_paint (render, &rotate->paint, cr); + + cairo_restore (cr); + + return status; +} + +static cairo_status_t +draw_paint_scale (cairo_colr_glyph_render_t *render, + FT_PaintScale *scale, + cairo_t *cr) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintScale\n", 2 * render->level, ""); +#endif + + cairo_save (cr); + + cairo_translate (cr, double_from_16_16 (scale->center_x), double_from_16_16 (scale->center_y)); + cairo_scale (cr, double_from_16_16 (scale->scale_x), double_from_16_16 (scale->scale_y)); + cairo_translate (cr, - double_from_16_16 (scale->center_x), - double_from_16_16 (scale->center_y)); + status = draw_paint (render, &scale->paint, cr); + + cairo_restore (cr); + + return status; +} + +static cairo_status_t +draw_paint_skew (cairo_colr_glyph_render_t *render, + FT_PaintSkew *skew, + cairo_t *cr) +{ + cairo_matrix_t s; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + +#if DEBUG_COLR + printf ("%*sDraw PaintSkew\n", 2 * render->level, ""); +#endif + + cairo_save (cr); + + cairo_translate (cr, double_from_16_16 (skew->center_x), double_from_16_16 (skew->center_y)); + cairo_matrix_init (&s, 1., tan (double_from_16_16 (skew->y_skew_angle) * M_PI), - tan (double_from_16_16 (skew->x_skew_angle) * M_PI), 1., 0., 0.); + cairo_transform (cr, &s); + cairo_translate (cr, - double_from_16_16 (skew->center_x), - double_from_16_16 (skew->center_y)); + status = draw_paint (render, &skew->paint, cr); + + cairo_restore (cr); + + return status; +} + +static cairo_status_t +draw_paint_composite (cairo_colr_glyph_render_t *render, + FT_PaintComposite *composite, + cairo_t *cr) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + +#if DEBUG_COLR + printf ("%*sDraw PaintComposite\n", 2 * render->level, ""); +#endif + + cairo_save (cr); + + status = draw_paint (render, &composite->backdrop_paint, cr); + if (unlikely (status)) { + cairo_pattern_destroy (cairo_pop_group (cr)); + goto cleanup; + } + + cairo_push_group (cr); + status = draw_paint (render, &composite->source_paint, cr); + if (unlikely (status)) { + cairo_pattern_destroy (cairo_pop_group (cr)); + cairo_pattern_destroy (cairo_pop_group (cr)); + goto cleanup; + } + + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, cairo_operator_from_ft_composite_mode (composite->composite_mode)); + cairo_paint (cr); + + cleanup: + cairo_restore (cr); + + return status; +} + +static cairo_status_t +draw_paint (cairo_colr_glyph_render_t *render, + FT_OpaquePaint *paint, + cairo_t *cr) +{ + FT_COLR_Paint p; + FT_Size orig_size; + FT_Size unscaled_size; + FT_Matrix orig_transform; + FT_Vector orig_delta; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + assert (cairo_status (cr) == CAIRO_STATUS_SUCCESS); + + if (!FT_Get_Paint (render->face, *paint, &p)) + return CAIRO_STATUS_NO_MEMORY; + + if (render->level == 0) { + /* Now that the FT_Get_Paint call has applied the root transform, + * make the face unscaled and untransformed, so we can load glyph + * contours. + */ + + FT_Matrix transform; + FT_Vector delta; + + orig_size = render->face->size; + FT_New_Size (render->face, &unscaled_size); + FT_Activate_Size (unscaled_size); + FT_Set_Char_Size (render->face, render->face->units_per_EM << 6, 0, 0, 0); + + transform.xx = transform.yy = 1 << 16; + transform.xy = transform.yx = 0; + delta.x = delta.y = 0; + + FT_Get_Transform (render->face, &orig_transform, &orig_delta); + FT_Set_Transform (render->face, &transform, &delta); + } + + render->level++; + + switch (p.format) { + case FT_COLR_PAINTFORMAT_COLR_LAYERS: + status = draw_paint_colr_layers (render, &p.u.colr_layers, cr); + break; + case FT_COLR_PAINTFORMAT_SOLID: + status = draw_paint_solid (render, &p.u.solid, cr); + break; + case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: + status = draw_paint_linear_gradient (render, &p.u.linear_gradient, cr); + break; + case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: + status = draw_paint_radial_gradient (render, &p.u.radial_gradient, cr); + break; + case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: + status = draw_paint_sweep_gradient (render, &p.u.sweep_gradient, cr); + break; + case FT_COLR_PAINTFORMAT_GLYPH: + status = draw_paint_glyph (render, &p.u.glyph, cr); + break; + case FT_COLR_PAINTFORMAT_COLR_GLYPH: + status = draw_paint_colr_glyph (render, &p.u.colr_glyph, cr); + break; + case FT_COLR_PAINTFORMAT_TRANSFORM: + status = draw_paint_transform (render, &p.u.transform, cr); + break; + case FT_COLR_PAINTFORMAT_TRANSLATE: + status = draw_paint_translate (render, &p.u.translate, cr); + break; + case FT_COLR_PAINTFORMAT_ROTATE: + status = draw_paint_rotate (render, &p.u.rotate, cr); + break; + case FT_COLR_PAINTFORMAT_SCALE: + status = draw_paint_scale (render, &p.u.scale, cr); + break; + case FT_COLR_PAINTFORMAT_SKEW: + status = draw_paint_skew (render, &p.u.skew, cr); + break; + case FT_COLR_PAINTFORMAT_COMPOSITE: + status = draw_paint_composite (render, &p.u.composite, cr); + break; + case FT_COLR_PAINT_FORMAT_MAX: + case FT_COLR_PAINTFORMAT_UNSUPPORTED: + default: + ASSERT_NOT_REACHED; + } + + render->level--; + + if (render->level == 0) { + FT_Set_Transform (render->face, &orig_transform, &orig_delta); + FT_Activate_Size (orig_size); + FT_Done_Size (unscaled_size); + } + + return status; +} + +static cairo_status_t +draw_colr_glyph (cairo_colr_glyph_render_t *render, + unsigned long glyph, + FT_Color_Root_Transform root, + cairo_t *cr) +{ + FT_OpaquePaint paint = { NULL, 0 }; + FT_ClipBox box; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + cairo_save (cr); + + if (FT_Get_Color_Glyph_ClipBox (render->face, glyph, &box)) { + double xmin, ymin, xmax, ymax; + + xmin = double_from_26_6 (box.bottom_left.x); + ymin = double_from_26_6 (box.bottom_left.y); + xmax = double_from_26_6 (box.top_right.x); + ymax = double_from_26_6 (box.top_right.y); + + cairo_new_path (cr); + cairo_rectangle (cr, xmin, ymin, xmax - xmin, ymax - ymin); + cairo_clip (cr); + } + + if (FT_Get_Color_Glyph_Paint (render->face, glyph, root, &paint)) + status = draw_paint (render, &paint, cr); + + cairo_restore (cr); + + return status; +} + +/* Create an image surface and render the glyph onto it, + * using the given colors. + */ +cairo_status_t +_cairo_render_colr_v1_glyph (FT_Face face, + unsigned long glyph, + FT_Color *palette, + int num_palette_entries, + cairo_t *cr, + cairo_pattern_t *foreground_source, + cairo_bool_t *foreground_source_used) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_colr_glyph_render_t colr_render; + +#if DEBUG_COLR + printf ("_cairo_render_colr_glyph glyph index: %ld\n", glyph); +#endif + + colr_render.face = face; + colr_render.palette = palette; + colr_render.num_palette_entries = num_palette_entries; + colr_render.foreground_marker = _cairo_pattern_create_foreground_marker (); + colr_render.foreground_source = cairo_pattern_reference (foreground_source);; + colr_render.foreground_source_used = FALSE; + colr_render.level = 0; + + status = draw_colr_glyph (&colr_render, + glyph, + FT_COLOR_INCLUDE_ROOT_TRANSFORM, + cr); + + cairo_pattern_destroy (colr_render.foreground_marker); + cairo_pattern_destroy (colr_render.foreground_source); + *foreground_source_used = colr_render.foreground_source_used; + + return status; +} + +#endif /* HAVE_FT_COLR_V1 */ diff --git a/src/cairo-debug.c b/src/cairo-debug.c index a314eefbf..11777f04b 100644 --- a/src/cairo-debug.c +++ b/src/cairo-debug.c @@ -319,3 +319,102 @@ _cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect) rect->x, rect->y, rect->width, rect->height); } + +const char * +_cairo_debug_operator_to_string (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: return "CLEAR"; + case CAIRO_OPERATOR_SOURCE: return "SOURCE"; + case CAIRO_OPERATOR_OVER: return "OVER"; + case CAIRO_OPERATOR_IN: return "IN"; + case CAIRO_OPERATOR_OUT: return "OUT"; + case CAIRO_OPERATOR_ATOP: return "ATOP"; + case CAIRO_OPERATOR_DEST: return "DEST"; + case CAIRO_OPERATOR_DEST_OVER: return "DEST_OVER"; + case CAIRO_OPERATOR_DEST_IN: return "DEST_IN"; + case CAIRO_OPERATOR_DEST_OUT: return "DEST_OUT"; + case CAIRO_OPERATOR_DEST_ATOP: return "DEST_ATOP"; + case CAIRO_OPERATOR_XOR: return "XOR"; + case CAIRO_OPERATOR_ADD: return "ADD"; + case CAIRO_OPERATOR_SATURATE: return "SATURATE"; + case CAIRO_OPERATOR_MULTIPLY: return "MULTIPLY"; + case CAIRO_OPERATOR_SCREEN: return "SCREEN"; + case CAIRO_OPERATOR_OVERLAY: return "OVERLAY"; + case CAIRO_OPERATOR_DARKEN: return "DARKEN"; + case CAIRO_OPERATOR_LIGHTEN: return "LIGHTEN"; + case CAIRO_OPERATOR_COLOR_DODGE: return "COLOR_DODGE"; + case CAIRO_OPERATOR_COLOR_BURN: return "COLOR_BURN"; + case CAIRO_OPERATOR_HARD_LIGHT: return "HARD_LIGHT"; + case CAIRO_OPERATOR_SOFT_LIGHT: return "SOFT_LIGHT"; + case CAIRO_OPERATOR_DIFFERENCE: return "DIFFERENCE"; + case CAIRO_OPERATOR_EXCLUSION: return "EXCLUSION"; + case CAIRO_OPERATOR_HSL_HUE: return "HSL_HUE"; + case CAIRO_OPERATOR_HSL_SATURATION: return "HSL_SATURATION"; + case CAIRO_OPERATOR_HSL_COLOR: return "HSL_COLOR"; + case CAIRO_OPERATOR_HSL_LUMINOSITY: return "HSL_LUMINOSITY"; + } + return "UNKNOWN"; +} + +const char * +_cairo_debug_status_to_string (cairo_int_status_t status) +{ + switch (status) { + case CAIRO_INT_STATUS_SUCCESS: return "SUCCESS"; + case CAIRO_INT_STATUS_NO_MEMORY: return "NO_MEMORY"; + case CAIRO_INT_STATUS_INVALID_RESTORE: return "INVALID_RESTORE"; + case CAIRO_INT_STATUS_INVALID_POP_GROUP: return "INVALID_POP_GROUP"; + case CAIRO_INT_STATUS_NO_CURRENT_POINT: return "NO_CURRENT_POINT"; + case CAIRO_INT_STATUS_INVALID_MATRIX: return "INVALID_MATRIX"; + case CAIRO_INT_STATUS_INVALID_STATUS: return "INVALID_STATUS"; + case CAIRO_INT_STATUS_NULL_POINTER: return "NULL_POINTER"; + case CAIRO_INT_STATUS_INVALID_STRING: return "INVALID_STRING"; + case CAIRO_INT_STATUS_INVALID_PATH_DATA: return "INVALID_PATH_DATA"; + case CAIRO_INT_STATUS_READ_ERROR: return "READ_ERROR"; + case CAIRO_INT_STATUS_WRITE_ERROR: return "WRITE_ERROR"; + case CAIRO_INT_STATUS_SURFACE_FINISHED: return "SURFACE_FINISHED"; + case CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH: return "SURFACE_TYPE_MISMATCH"; + case CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH: return "PATTERN_TYPE_MISMATCH"; + case CAIRO_INT_STATUS_INVALID_CONTENT: return "INVALID_CONTENT"; + case CAIRO_INT_STATUS_INVALID_FORMAT: return "INVALID_FORMAT"; + case CAIRO_INT_STATUS_INVALID_VISUAL: return "INVALID_VISUAL"; + case CAIRO_INT_STATUS_FILE_NOT_FOUND: return "FILE_NOT_FOUND"; + case CAIRO_INT_STATUS_INVALID_DASH: return "INVALID_DASH"; + case CAIRO_INT_STATUS_INVALID_DSC_COMMENT: return "INVALID_DSC_COMMENT"; + case CAIRO_INT_STATUS_INVALID_INDEX: return "INVALID_INDEX"; + case CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE: return "CLIP_NOT_REPRESENTABLE"; + case CAIRO_INT_STATUS_TEMP_FILE_ERROR: return "TEMP_FILE_ERROR"; + case CAIRO_INT_STATUS_INVALID_STRIDE: return "INVALID_STRIDE"; + case CAIRO_INT_STATUS_FONT_TYPE_MISMATCH: return "FONT_TYPE_MISMATCH"; + case CAIRO_INT_STATUS_USER_FONT_IMMUTABLE: return "USER_FONT_IMMUTABLE"; + case CAIRO_INT_STATUS_USER_FONT_ERROR: return "USER_FONT_ERROR"; + case CAIRO_INT_STATUS_NEGATIVE_COUNT: return "NEGATIVE_COUNT"; + case CAIRO_INT_STATUS_INVALID_CLUSTERS: return "INVALID_CLUSTERS"; + case CAIRO_INT_STATUS_INVALID_SLANT: return "INVALID_SLANT"; + case CAIRO_INT_STATUS_INVALID_WEIGHT: return "INVALID_WEIGHT"; + case CAIRO_INT_STATUS_INVALID_SIZE: return "INVALID_SIZE"; + case CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED: return "USER_FONT_NOT_IMPLEMENTED"; + case CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH: return "DEVICE_TYPE_MISMATCH"; + case CAIRO_INT_STATUS_DEVICE_ERROR: return "DEVICE_ERROR"; + case CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION: return "INVALID_MESH_CONSTRUCTION"; + case CAIRO_INT_STATUS_DEVICE_FINISHED: return "DEVICE_FINISHED"; + case CAIRO_INT_STATUS_JBIG2_GLOBAL_MISSING: return "JBIG2_GLOBAL_MISSING"; + case CAIRO_INT_STATUS_PNG_ERROR: return "PNG_ERROR"; + case CAIRO_INT_STATUS_FREETYPE_ERROR: return "FREETYPE_ERROR"; + case CAIRO_INT_STATUS_WIN32_GDI_ERROR: return "WIN32_GDI_ERROR"; + case CAIRO_INT_STATUS_TAG_ERROR: return "TAG_ERROR"; + case CAIRO_INT_STATUS_DWRITE_ERROR: return "DWRITE_ERROR"; + case CAIRO_INT_STATUS_SVG_FONT_ERROR: return "SVG_FONT_ERROR"; + + case CAIRO_INT_STATUS_LAST_STATUS: return "LAST_STATUS"; + + case CAIRO_INT_STATUS_UNSUPPORTED: return "UNSUPPORTED"; + case CAIRO_INT_STATUS_DEGENERATE: return "DEGENERATE"; + case CAIRO_INT_STATUS_NOTHING_TO_DO: return "NOTHING_TO_DO"; + case CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY: return "FLATTEN_TRANSPARENCY"; + case CAIRO_INT_STATUS_IMAGE_FALLBACK: return "IMAGE_FALLBACK"; + case CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN: return "ANALYZE_RECORDING_SURFACE_PATTERN"; + } + return "UNKNOWN"; +} diff --git a/src/cairo-device.c b/src/cairo-device.c index 50e7ee484..57b63a778 100644 --- a/src/cairo-device.c +++ b/src/cairo-device.c @@ -164,6 +164,7 @@ _cairo_device_create_in_error (cairo_status_t status) case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_STATUS_TAG_ERROR: case CAIRO_STATUS_DWRITE_ERROR: + case CAIRO_STATUS_SVG_FONT_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_device_t *) &_nil_device; diff --git a/src/cairo-xml.h b/src/cairo-dwrite.h index 9ae76e90a..b1ff718a0 100644 --- a/src/cairo-xml.h +++ b/src/cairo-dwrite.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2009 Chris Wilson + * Copyright © 2023 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 @@ -27,41 +27,46 @@ * * 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> + * Adrian Johnson <ajohnson@redneon.com> */ -#ifndef CAIRO_XML_H -#define CAIRO_XML_H +#ifndef _CAIRO_DWRITE_H_ +#define _CAIRO_DWRITE_H_ #include "cairo.h" -#if CAIRO_HAS_XML_SURFACE +#if CAIRO_HAS_DWRITE_FONT + +#ifdef __cplusplus + +#include <dwrite.h> CAIRO_BEGIN_DECLS -cairo_public cairo_device_t * -cairo_xml_create (const char *filename); +cairo_public cairo_font_face_t * +cairo_dwrite_font_face_create_for_dwrite_fontface (IDWriteFontFace *dwrite_font_face); -cairo_public cairo_device_t * -cairo_xml_create_for_stream (cairo_write_func_t write_func, - void *closure); +cairo_public IDWriteRenderingParams * +cairo_dwrite_font_face_get_rendering_params (cairo_font_face_t *font_face); -cairo_public cairo_surface_t * -cairo_xml_surface_create (cairo_device_t *xml, - cairo_content_t content, - double width, double height); +cairo_public void +cairo_dwrite_font_face_set_rendering_params (cairo_font_face_t *font_face, IDWriteRenderingParams *params); -cairo_public cairo_status_t -cairo_xml_for_recording_surface (cairo_device_t *xml, - cairo_surface_t *surface); +cairo_public DWRITE_MEASURING_MODE +cairo_dwrite_font_face_get_measuring_mode (cairo_font_face_t *font_face); + +cairo_public void +cairo_dwrite_font_face_set_measuring_mode (cairo_font_face_t *font_face, DWRITE_MEASURING_MODE mode); CAIRO_END_DECLS -#else /*CAIRO_HAS_XML_SURFACE*/ -# error Cairo was not compiled with support for the XML backend -#endif /*CAIRO_HAS_XML_SURFACE*/ +#else /* __cplusplus */ +#error DWrite font backend requires C++ +#endif /* __cplusplus */ + +#else /* CAIRO_HAS_DWRITE_FONT */ +# error Cairo was not compiled with support for DWrite font backend +#endif /* CAIRO_HAS_DWRITE_FONT */ -#endif /*CAIRO_XML_H*/ +#endif /* _CAIRO_DWRITE_H_ */ diff --git a/src/cairo-egl-context.c b/src/cairo-egl-context.c deleted file mode 100644 index bf704c630..000000000 --- a/src/cairo-egl-context.c +++ /dev/null @@ -1,317 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005 Red Hat, Inc - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-error-private.h" - -typedef struct _cairo_egl_context { - cairo_gl_context_t base; - - EGLDisplay display; - EGLContext context; - - EGLSurface dummy_surface; - - EGLContext previous_context; - EGLSurface previous_surface; -} cairo_egl_context_t; - -typedef struct _cairo_egl_surface { - cairo_gl_surface_t base; - - EGLSurface egl; -} cairo_egl_surface_t; - - -static cairo_bool_t -_context_acquisition_changed_egl_state (cairo_egl_context_t *ctx, - EGLSurface current_surface) -{ - return ctx->previous_context != ctx->context || - ctx->previous_surface != current_surface; -} - -static EGLSurface -_egl_get_current_surface (cairo_egl_context_t *ctx) -{ - if (ctx->base.current_target == NULL || - _cairo_gl_surface_is_texture (ctx->base.current_target)) { - return ctx->dummy_surface; - } - - return ((cairo_egl_surface_t *) ctx->base.current_target)->egl; -} - -static void -_egl_query_current_state (cairo_egl_context_t *ctx) -{ - ctx->previous_surface = eglGetCurrentSurface (EGL_DRAW); - ctx->previous_context = eglGetCurrentContext (); - - /* If any of the values were none, assume they are all none. Not all - drivers seem well behaved when it comes to using these values across - multiple threads. */ - if (ctx->previous_surface == EGL_NO_SURFACE || - ctx->previous_context == EGL_NO_CONTEXT) { - ctx->previous_surface = EGL_NO_SURFACE; - ctx->previous_context = EGL_NO_CONTEXT; - } -} - -static void -_egl_acquire (void *abstract_ctx) -{ - cairo_egl_context_t *ctx = abstract_ctx; - EGLSurface current_surface = _egl_get_current_surface (ctx); - - _egl_query_current_state (ctx); - if (!_context_acquisition_changed_egl_state (ctx, current_surface)) - return; - - eglMakeCurrent (ctx->display, - current_surface, current_surface, ctx->context); -} - -static void -_egl_release (void *abstract_ctx) -{ - cairo_egl_context_t *ctx = abstract_ctx; - if (!ctx->base.thread_aware || - !_context_acquisition_changed_egl_state (ctx, - _egl_get_current_surface (ctx))) { - return; - } - - eglMakeCurrent (ctx->display, - EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); -} - -static void -_egl_make_current (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_egl_context_t *ctx = abstract_ctx; - cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface; - - eglMakeCurrent(ctx->display, surface->egl, surface->egl, ctx->context); -} - -static void -_egl_swap_buffers (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_egl_context_t *ctx = abstract_ctx; - cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface; - - eglSwapBuffers (ctx->display, surface->egl); -} - -static void -_egl_destroy (void *abstract_ctx) -{ - cairo_egl_context_t *ctx = abstract_ctx; - - eglMakeCurrent (ctx->display, - EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (ctx->dummy_surface != EGL_NO_SURFACE) - eglDestroySurface (ctx->display, ctx->dummy_surface); -} - -static cairo_bool_t -_egl_make_current_surfaceless(cairo_egl_context_t *ctx) -{ - const char *extensions; - - extensions = eglQueryString(ctx->display, EGL_EXTENSIONS); - if (strstr(extensions, "EGL_KHR_surfaceless_context") == NULL && - strstr(extensions, "EGL_KHR_surfaceless_opengl") == NULL) - return FALSE; - - if (!eglMakeCurrent(ctx->display, - EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->context)) - return FALSE; - - return TRUE; -} - -cairo_device_t * -cairo_egl_device_create (EGLDisplay dpy, EGLContext egl) -{ - cairo_egl_context_t *ctx; - cairo_status_t status; - int attribs[] = { - EGL_WIDTH, 1, - EGL_HEIGHT, 1, - EGL_NONE, - }; - EGLConfig config; - EGLint numConfigs; - - ctx = calloc (1, sizeof (cairo_egl_context_t)); - if (unlikely (ctx == NULL)) - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - - ctx->display = dpy; - ctx->context = egl; - - ctx->base.acquire = _egl_acquire; - ctx->base.release = _egl_release; - ctx->base.make_current = _egl_make_current; - ctx->base.swap_buffers = _egl_swap_buffers; - ctx->base.destroy = _egl_destroy; - - /* We are about the change the current state of EGL, so we should - * query the pre-existing surface now instead of later. */ - _egl_query_current_state (ctx); - - if (!_egl_make_current_surfaceless (ctx)) { - /* Fall back to dummy surface, meh. */ - EGLint config_attribs[] = { - EGL_CONFIG_ID, 0, - EGL_NONE - }; - - /* - * In order to be able to make an egl context current when using a - * pbuffer surface, that surface must have been created with a config - * that is compatible with the context config. For Mesa, this means - * that the configs must be the same. - */ - eglQueryContext (dpy, egl, EGL_CONFIG_ID, &config_attribs[1]); - eglChooseConfig (dpy, config_attribs, &config, 1, &numConfigs); - - ctx->dummy_surface = eglCreatePbufferSurface (dpy, config, attribs); - if (ctx->dummy_surface == NULL) { - free (ctx); - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - } - - if (!eglMakeCurrent (dpy, ctx->dummy_surface, ctx->dummy_surface, egl)) { - free (ctx); - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - } - } - - status = _cairo_gl_dispatch_init (&ctx->base.dispatch, eglGetProcAddress); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - status = _cairo_gl_context_init (&ctx->base); - if (unlikely (status)) { - if (ctx->dummy_surface != EGL_NO_SURFACE) - eglDestroySurface (dpy, ctx->dummy_surface); - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - /* Tune the default VBO size to reduce overhead on embedded devices. - * This smaller size means that flushing needs to be done more often, - * but it is less demanding of scarce memory on embedded devices. - */ - ctx->base.vbo_size = 16*1024; - - eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - return &ctx->base.base; -} - -cairo_surface_t * -cairo_gl_surface_create_for_egl (cairo_device_t *device, - EGLSurface egl, - int width, - int height) -{ - cairo_egl_surface_t *surface; - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - if (width <= 0 || height <= 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - surface = calloc (1, sizeof (cairo_egl_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (device, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - surface->egl = egl; - - return &surface->base.base; -} - -static cairo_bool_t is_egl_device (cairo_device_t *device) -{ - return (device->backend != NULL && - device->backend->type == CAIRO_DEVICE_TYPE_GL); -} - -static cairo_egl_context_t *to_egl_context (cairo_device_t *device) -{ - return (cairo_egl_context_t *) device; -} - -EGLDisplay -cairo_egl_device_get_display (cairo_device_t *device) -{ - if (! is_egl_device (device)) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return EGL_NO_DISPLAY; - } - - return to_egl_context (device)->display; -} - -cairo_public EGLContext -cairo_egl_device_get_context (cairo_device_t *device) -{ - if (! is_egl_device (device)) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return EGL_NO_CONTEXT; - } - - return to_egl_context (device)->context; -} diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h index d84d4c23d..ba95db767 100644 --- a/src/cairo-error-private.h +++ b/src/cairo-error-private.h @@ -99,6 +99,7 @@ enum _cairo_int_status { CAIRO_INT_STATUS_WIN32_GDI_ERROR, CAIRO_INT_STATUS_TAG_ERROR, CAIRO_INT_STATUS_DWRITE_ERROR, + CAIRO_INT_STATUS_SVG_FONT_ERROR, CAIRO_INT_STATUS_LAST_STATUS, diff --git a/src/cairo-features-uninstalled.pc.in b/src/cairo-features-uninstalled.pc.in deleted file mode 100644 index b9cd9d3ad..000000000 --- a/src/cairo-features-uninstalled.pc.in +++ /dev/null @@ -1,7 +0,0 @@ -Name: @FEATURE_PC@ -Description: @FEATURE_NAME@ for cairo graphics library -Version: @VERSION@ - -Requires: @FEATURE_BASE@ @FEATURE_REQUIRES@ -Libs: @FEATURE_NONPKGCONFIG_LIBS@ @FEATURE_NONPKGCONFIG_EXTRA_LIBS@ -Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@/src @FEATURE_NONPKGCONFIG_CFLAGS@ diff --git a/src/cairo-features.pc.in b/src/cairo-features.pc.in deleted file mode 100644 index 9a4b657c8..000000000 --- a/src/cairo-features.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: @FEATURE_PC@ -Description: @FEATURE_NAME@ for cairo graphics library -Version: @VERSION@ - -Requires: @FEATURE_BASE@ @FEATURE_REQUIRES@ -Libs: @FEATURE_NONPKGCONFIG_LIBS@ @FEATURE_NONPKGCONFIG_EXTRA_LIBS@ -Cflags: -I${includedir}/cairo @FEATURE_NONPKGCONFIG_CFLAGS@ diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c index 30d695894..33ee617b8 100644 --- a/src/cairo-font-options.c +++ b/src/cairo-font-options.c @@ -58,7 +58,8 @@ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_ROUND_GLYPH_POS_DEFAULT, NULL, /* variations */ CAIRO_COLOR_MODE_DEFAULT, - CAIRO_COLOR_PALETTE_DEFAULT + CAIRO_COLOR_PALETTE_DEFAULT, + NULL, 0, /* custom palette */ }; /** @@ -79,6 +80,8 @@ _cairo_font_options_init_default (cairo_font_options_t *options) options->variations = NULL; options->color_mode = CAIRO_COLOR_MODE_DEFAULT; options->palette_index = CAIRO_COLOR_PALETTE_DEFAULT; + options->custom_palette = NULL; + options->custom_palette_size = 0; } void @@ -94,6 +97,47 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, options->variations = other->variations ? strdup (other->variations) : NULL; options->color_mode = other->color_mode; options->palette_index = other->palette_index; + options->custom_palette_size = other->custom_palette_size; + options->custom_palette = NULL; + if (other->custom_palette) { + options->custom_palette = (cairo_palette_color_t *) malloc (sizeof (cairo_palette_color_t) * options->custom_palette_size); + memcpy (options->custom_palette, other->custom_palette, sizeof (cairo_palette_color_t) * options->custom_palette_size); + } +} + +cairo_bool_t +_cairo_font_options_compare (const cairo_font_options_t *a, + const cairo_font_options_t *b) +{ + if (a->antialias != b->antialias || + a->subpixel_order != b->subpixel_order || + a->lcd_filter != b->lcd_filter || + a->hint_style != b->hint_style || + a->hint_metrics != b->hint_metrics || + a->round_glyph_positions != b->round_glyph_positions || + a->color_mode != b->color_mode || + a->palette_index != b->palette_index || + a->custom_palette_size != b->custom_palette_size) + { + return FALSE; + } + + if (a->variations && b->variations && strcmp (a->variations, b->variations) != 0) + return FALSE; + else if (a->variations != b->variations) + return FALSE; + + if (a->custom_palette && b->custom_palette && + memcmp (a->custom_palette, b->custom_palette, sizeof (cairo_palette_color_t) * a->custom_palette_size) != 0) + { + return FALSE; + } + else if (a->custom_palette != b->custom_palette) + { + return FALSE; + } + + return TRUE; } /** @@ -164,6 +208,7 @@ void _cairo_font_options_fini (cairo_font_options_t *options) { free (options->variations); + free (options->custom_palette); } /** @@ -266,6 +311,12 @@ cairo_font_options_merge (cairo_font_options_t *options, options->color_mode = other->color_mode; if (other->palette_index != CAIRO_COLOR_PALETTE_DEFAULT) options->palette_index = other->palette_index; + if (other->custom_palette) { + options->custom_palette_size = other->custom_palette_size; + free (options->custom_palette); + options->custom_palette = (cairo_palette_color_t *) malloc (sizeof (cairo_palette_color_t) * options->custom_palette_size); + memcpy (options->custom_palette, other->custom_palette, sizeof (cairo_palette_color_t) * options->custom_palette_size); + } } slim_hidden_def (cairo_font_options_merge); @@ -304,7 +355,12 @@ cairo_font_options_equal (const cairo_font_options_t *options, (options->variations != NULL && other->variations != NULL && strcmp (options->variations, other->variations) == 0)) && options->color_mode == other->color_mode && - options->palette_index == other->palette_index); + options->palette_index == other->palette_index && + ((options->custom_palette == NULL && other->custom_palette == NULL) || + (options->custom_palette != NULL && other->custom_palette != NULL && + options->custom_palette_size == other->custom_palette_size && + memcmp (options->custom_palette, other->custom_palette, + sizeof (cairo_palette_color_t) * options->custom_palette_size) == 0))); } slim_hidden_def (cairo_font_options_equal); @@ -641,7 +697,7 @@ cairo_font_options_get_variations (cairo_font_options_t *options) /** * cairo_font_options_set_color_mode: * @options: a #cairo_font_options_t - * @font_color: the new color mode + * @color_mode: the new color mode * * Sets the color mode for the font options object. This controls * whether color fonts are to be rendered in color or as outlines. @@ -680,14 +736,26 @@ cairo_font_options_get_color_mode (const cairo_font_options_t *options) } /** + * CAIRO_COLOR_PALETTE_DEFAULT: + * + * The default color palette index. + * + * Since: 1.18 + **/ + +/** * cairo_font_options_set_color_palette: * @options: a #cairo_font_options_t * @palette_index: the palette index in the CPAL table * * Sets the OpenType font color palette for the font options * object. OpenType color fonts with a CPAL table may contain multiple - * palettes. The default color palette index is %CAIRO_COLOR_PALETTE_DEFAULT. If - * @palette_index is invalid, the default palette is used. + * palettes. The default color palette index is %CAIRO_COLOR_PALETTE_DEFAULT. + * + * If @palette_index is invalid, the default palette is used. + * + * Individual colors within the palette may be overriden with + * cairo_font_options_set_custom_palette_color(). * * Since: 1.18 **/ @@ -705,7 +773,7 @@ cairo_font_options_set_color_palette (cairo_font_options_t *options, * cairo_font_options_get_color_palette: * @options: a #cairo_font_options_t * - * Gets the OpenType color font palette for the font options object. + * Gets the current OpenType color font palette for the font options object. * * Return value: the palette index * @@ -719,3 +787,94 @@ cairo_font_options_get_color_palette (const cairo_font_options_t *options) return options->palette_index; } + +/** + * cairo_font_options_set_custom_palette_color: + * @options: a #cairo_font_options_t + * @index: the index of the color to set + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Sets a custom palette color for the font options object. This + * overrides the palette color at the specified color index. This override is + * independent of the selected palette index and will remain in place + * even if cairo_font_options_set_color_palette() is called to change + * the palette index. + * + * It is only possible to override color indexes already in the font + * palette. + * + * Since: 1.18 + */ +void +cairo_font_options_set_custom_palette_color (cairo_font_options_t *options, + unsigned int index, + double red, double green, + double blue, double alpha) +{ + unsigned int idx; + + for (idx = 0; idx < options->custom_palette_size; idx++) { + if (options->custom_palette[idx].index == index) { + break; + } + } + + if (idx == options->custom_palette_size) { + options->custom_palette_size++; + options->custom_palette = (cairo_palette_color_t *) + _cairo_realloc_ab (options->custom_palette, + sizeof (cairo_palette_color_t), + options->custom_palette_size); + } + + /* beware of holes */ + memset (&options->custom_palette[idx], 0, sizeof (cairo_palette_color_t)); + + options->custom_palette[idx].index = index; + options->custom_palette[idx].red = red; + options->custom_palette[idx].green = green; + options->custom_palette[idx].blue = blue; + options->custom_palette[idx].alpha = alpha; +} + +/** + * cairo_font_options_get_custom_palette_color: + * @options: a #cairo_font_options_t + * @index: the index of the color to get + * @red: return location for red component of color + * @green: return location for green component of color + * @blue: return location for blue component of color + * @alpha: return location for alpha component of color + * + * Gets the custom palette color for the color index for the font options object. + * + * Returns: `CAIRO_STATUS_SUCCESS` if a custom palette color is + * returned, `CAIRO_STATUS_INVALID_INDEX` if no custom color exists + * for the color index. + * + * Since: 1.18 + */ +cairo_status_t +cairo_font_options_get_custom_palette_color (cairo_font_options_t *options, + unsigned int index, + double *red, double *green, + double *blue, double *alpha) +{ + unsigned int idx; + + for (idx = 0; idx < options->custom_palette_size; idx++) { + if (options->custom_palette[idx].index == index) { + *red = options->custom_palette[idx].red; + *green = options->custom_palette[idx].green; + *blue = options->custom_palette[idx].blue; + *alpha = options->custom_palette[idx].alpha; + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_STATUS_INVALID_INDEX; +} +slim_hidden_def (cairo_font_options_get_custom_palette_color); diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index cdb02ff65..22a6a622b 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -44,8 +44,11 @@ #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-ft-private.h" +#include "cairo-list-inline.h" +#include "cairo-path-private.h" #include "cairo-pattern-private.h" #include "cairo-pixman-private.h" +#include "cairo-recording-surface-private.h" #include <float.h> @@ -67,6 +70,10 @@ #include FT_LCD_FILTER_H #endif +#if HAVE_FT_SVG_DOCUMENT +#include FT_OTSVG_H +#endif + #if HAVE_UNISTD_H #include <unistd.h> #elif !defined(access) @@ -241,17 +248,19 @@ _cairo_ft_resolve_pattern (FcPattern *pattern, #endif -static cairo_status_t -_ft_to_cairo_error (FT_Error error) +cairo_status_t +_cairo_ft_to_cairo_error (FT_Error error) { /* Currently we don't get many (any?) useful statuses here. * Populate as needed. */ switch (error) { - case FT_Err_Out_Of_Memory: - return CAIRO_STATUS_NO_MEMORY; - default: - return CAIRO_STATUS_FREETYPE_ERROR; + case FT_Err_Ok: + return CAIRO_STATUS_SUCCESS; + case FT_Err_Out_Of_Memory: + return CAIRO_STATUS_NO_MEMORY; + default: + return CAIRO_STATUS_FREETYPE_ERROR; } } @@ -751,7 +760,7 @@ _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) { unscaled->lock_count--; CAIRO_MUTEX_UNLOCK (unscaled->mutex); - _cairo_error_throw (_ft_to_cairo_error (error)); + _cairo_error_throw (_cairo_ft_to_cairo_error (error)); return NULL; } @@ -908,7 +917,7 @@ _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, sf.y_scale * 64.0 + .5, 0, 0); if (error) - return _cairo_error (_ft_to_cairo_error (error)); + return _cairo_error (_cairo_ft_to_cairo_error (error)); return CAIRO_STATUS_SUCCESS; } @@ -1350,7 +1359,7 @@ _get_bitmap_surface (FT_Bitmap *bitmap, error = FT_Bitmap_Convert( library, bitmap, &tmp, align ); if (error) - return _cairo_error (_ft_to_cairo_error (error)); + return _cairo_error (_cairo_ft_to_cairo_error (error)); FT_Bitmap_Done( library, bitmap ); *bitmap = tmp; @@ -1558,7 +1567,7 @@ _render_glyph_outline (FT_Face face, #endif if (error) - return _cairo_error (_ft_to_cairo_error (error)); + return _cairo_error (_cairo_ft_to_cairo_error (error)); bitmap_size = _compute_xrender_bitmap_size (&bitmap, face->glyph, @@ -2261,10 +2270,9 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2, return 0; } -static cairo_status_t -_decompose_glyph_outline (FT_Face face, - cairo_font_options_t *options, - cairo_path_fixed_t **pathp) +cairo_status_t +_cairo_ft_face_decompose_glyph_outline (FT_Face face, + cairo_path_fixed_t **pathp) { static const FT_Outline_Funcs outline_funcs = { (FT_Outline_MoveToFunc)_move_to, @@ -2486,6 +2494,110 @@ _cairo_ft_scaled_glyph_load_glyph (cairo_ft_scaled_font_t *scaled_font, return CAIRO_STATUS_SUCCESS; } +typedef enum { + CAIRO_FT_GLYPH_TYPE_BITMAP, + CAIRO_FT_GLYPH_TYPE_OUTLINE, + CAIRO_FT_GLYPH_TYPE_SVG, + CAIRO_FT_GLYPH_TYPE_COLR_V0, + CAIRO_FT_GLYPH_TYPE_COLR_V1, +} cairo_ft_glyph_format_t; + +typedef struct { + cairo_scaled_glyph_private_t base; + + cairo_ft_glyph_format_t format; +} cairo_ft_glyph_private_t; + +static void +_cairo_ft_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, + cairo_scaled_glyph_t *glyph, + cairo_scaled_font_t *font) +{ + cairo_list_del (&glyph_private->link); + free (glyph_private); +} + + +#ifdef HAVE_FT_PALETTE_SELECT +static void +_cairo_ft_scaled_glyph_set_palette (cairo_ft_scaled_font_t *scaled_font, + FT_Face face, + unsigned int *num_entries_ret, + FT_Color **entries_ret) +{ + FT_Palette_Data palette_data; + unsigned int num_entries; + FT_Color *entries; + + num_entries = 0; + entries = NULL; + + if (FT_Palette_Data_Get (face, &palette_data) == 0 && palette_data.num_palettes > 0) { + FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT; + if (scaled_font->base.options.palette_index < palette_data.num_palettes) + palette_index = scaled_font->base.options.palette_index; + + if (FT_Palette_Select (face, palette_index, &entries) == 0) { + num_entries = palette_data.num_palette_entries; + + /* Overlay custom colors */ + for (unsigned int i = 0; i < scaled_font->base.options.custom_palette_size; i++) { + cairo_palette_color_t *entry = &scaled_font->base.options.custom_palette[i]; + if (entry->index < num_entries) { + entries[entry->index].red = 255 * entry->red; + entries[entry->index].green = 255 * entry->green; + entries[entry->index].blue = 255 * entry->blue; + entries[entry->index].alpha = 255 * entry->alpha; + } + } + } + } + if (num_entries_ret) + *num_entries_ret = num_entries; + + if (entries_ret) + *entries_ret = entries; +} +#endif + +/* returns TRUE if foreground color used */ +static cairo_bool_t +_cairo_ft_scaled_glyph_set_foreground_color (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + const cairo_color_t *foreground_color) +{ + cairo_bool_t uses_foreground_color = FALSE; +#ifdef HAVE_FT_PALETTE_SELECT + FT_LayerIterator iterator; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + FT_Color color; + + /* Check if there is a layer that uses the foreground color */ + iterator.p = NULL; + while (FT_Get_Color_Glyph_Layer(face, + _cairo_scaled_glyph_index (scaled_glyph), + &layer_glyph_index, + &layer_color_index, + &iterator)) { + if (layer_color_index == 0xFFFF) { + uses_foreground_color = TRUE; + break; + } + } + + if (uses_foreground_color) { + color.red = (FT_Byte)(foreground_color->red * 0xFF); + color.green = (FT_Byte)(foreground_color->green * 0xFF); + color.blue = (FT_Byte)(foreground_color->blue * 0xFF); + color.alpha = (FT_Byte)(foreground_color->alpha * 0xFF); + FT_Palette_Set_Foreground_Color (face, color); + } +#endif + return uses_foreground_color; +} + static cairo_int_status_t _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph, @@ -2500,6 +2612,7 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, cairo_status_t status; cairo_image_surface_t *surface; cairo_bool_t uses_foreground_color = FALSE; + cairo_ft_glyph_private_t *glyph_priv = scaled_glyph->dev_private; /* Only one info type at a time handled in this function */ assert (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE || info == CAIRO_SCALED_GLYPH_INFO_SURFACE); @@ -2511,41 +2624,12 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, return CAIRO_INT_STATUS_UNSUPPORTED; } + uses_foreground_color = _cairo_ft_scaled_glyph_set_foreground_color (scaled_font, + scaled_glyph, + face, + foreground_color); #ifdef HAVE_FT_PALETTE_SELECT - FT_LayerIterator iterator; - FT_UInt layer_glyph_index; - FT_UInt layer_color_index; - FT_Color color; - FT_Palette_Data palette_data; - - /* Check if there is a layer that uses the foreground color */ - iterator.p = NULL; - while (FT_Get_Color_Glyph_Layer(face, - _cairo_scaled_glyph_index(scaled_glyph), - &layer_glyph_index, - &layer_color_index, - &iterator)) { - if (layer_color_index == 0xFFFF) { - uses_foreground_color = TRUE; - break; - } - } - - if (uses_foreground_color) { - color.red = (FT_Byte)(foreground_color->red * 0xFF); - color.green = (FT_Byte)(foreground_color->green * 0xFF); - color.blue = (FT_Byte)(foreground_color->blue * 0xFF); - color.alpha = (FT_Byte)(foreground_color->alpha * 0xFF); - FT_Palette_Set_Foreground_Color (face, color); - } - - if (FT_Palette_Data_Get(face, &palette_data) == 0 && palette_data.num_palettes > 0) { - FT_UShort palette_index = CAIRO_COLOR_PALETTE_DEFAULT; - if (scaled_font->base.options.palette_index < palette_data.num_palettes) - palette_index = scaled_font->base.options.palette_index; - - FT_Palette_Select (face, palette_index, NULL); - } + _cairo_ft_scaled_glyph_set_palette (scaled_font, face, NULL, NULL); #endif load_flags &= ~FT_LOAD_MONOCHROME; @@ -2572,7 +2656,9 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, glyph = face->glyph; - if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V0 || + glyph_priv->format == CAIRO_FT_GLYPH_TYPE_OUTLINE) { + status = _render_glyph_outline (face, &scaled_font->ft_options.base, &surface); } else { @@ -2584,10 +2670,11 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, if (unlikely (status)) cairo_surface_destroy (&surface->base); } - if (unlikely (status)) - return status; } + if (unlikely (status)) + return status; + if (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) { /* We tried loading a color glyph and can now check if we got * a color glyph and set scaled_glyph->color_glyph @@ -2598,7 +2685,7 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, _cairo_scaled_glyph_set_color_surface (scaled_glyph, &scaled_font->base, surface, - uses_foreground_color); + uses_foreground_color ? foreground_color : NULL); scaled_glyph->color_glyph = TRUE; } else { @@ -2621,22 +2708,712 @@ _cairo_ft_scaled_glyph_init_surface (cairo_ft_scaled_font_t *scaled_font, } static cairo_int_status_t +_cairo_ft_scaled_glyph_init_record_colr_v0_glyph (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + cairo_bool_t vertical_layout, + int load_flags) +{ +#ifdef HAVE_FT_PALETTE_SELECT + cairo_surface_t *recording_surface; + cairo_t *cr; + cairo_status_t status; + FT_Color *palette; + unsigned int num_palette_entries; + FT_LayerIterator iterator; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + cairo_path_fixed_t *path_fixed; + cairo_path_t *path; + + _cairo_ft_scaled_glyph_set_palette (scaled_font, face, &num_palette_entries, &palette); + + load_flags &= ~FT_LOAD_MONOCHROME; + /* clear load target mode */ + load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(load_flags))); + load_flags |= FT_LOAD_TARGET_NORMAL; + load_flags |= FT_LOAD_COLOR; + + recording_surface = + cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + + cr = cairo_create (recording_surface); + + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cairo_matrix_t scale; + scale = scaled_font->base.scale; + scale.x0 = scale.y0 = 0.; + cairo_set_matrix (cr, &scale); + } + + iterator.p = NULL; + while (FT_Get_Color_Glyph_Layer(face, + _cairo_scaled_glyph_index (scaled_glyph), + &layer_glyph_index, + &layer_color_index, + &iterator)) + { + cairo_pattern_t *pattern; + if (layer_color_index == 0xFFFF) { + pattern = _cairo_pattern_create_foreground_marker (); + } else { + double r = 0, g = 0, b = 0, a = 1; + if (layer_color_index < num_palette_entries) { + FT_Color *color = &palette[layer_color_index]; + r = color->red / 255.0; + g = color->green/ 255.0; + b = color->blue / 255.0; + a = color->alpha / 255.0; + } + pattern = cairo_pattern_create_rgba (r, g, b, a); + } + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + if (FT_Load_Glyph (face, layer_glyph_index, load_flags) != 0) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + status = _cairo_ft_face_decompose_glyph_outline (face, &path_fixed); + if (unlikely (status)) + return status; + + path = _cairo_path_create (path_fixed, cr); + _cairo_path_fixed_destroy (path_fixed); + cairo_append_path(cr, path); + cairo_path_destroy (path); + cairo_fill (cr); + } + + cleanup: + cairo_destroy (cr); + + if (status) { + cairo_surface_destroy (recording_surface); + return status; + } + + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface, + NULL); + return status; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_int_status_t +_cairo_ft_scaled_glyph_init_record_colr_v1_glyph (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + const cairo_color_t *foreground_color, + cairo_text_extents_t *extents) +{ +#if HAVE_FT_COLR_V1 + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_surface_t *recording_surface; + cairo_t *cr; + FT_Color *palette; + unsigned int num_palette_entries; + cairo_bool_t foreground_source_used = FALSE; + + recording_surface = + cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + + cairo_surface_set_device_scale (recording_surface, 1, -1); + + cr = cairo_create (recording_surface); + + cairo_set_font_size (cr, 1.0); + cairo_set_font_options (cr, &scaled_font->base.options); + + extents->x_bearing = DOUBLE_FROM_26_6(face->bbox.xMin); + extents->y_bearing = DOUBLE_FROM_26_6(face->bbox.yMin); + extents->width = DOUBLE_FROM_26_6(face->bbox.xMax) - extents->x_bearing; + extents->height = DOUBLE_FROM_26_6(face->bbox.yMax) - extents->y_bearing; + + _cairo_ft_scaled_glyph_set_palette (scaled_font, face, &num_palette_entries, &palette); + + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cairo_pattern_t *foreground_pattern = _cairo_pattern_create_solid (foreground_color); + status = _cairo_render_colr_v1_glyph (face, + _cairo_scaled_glyph_index (scaled_glyph), + palette, + num_palette_entries, + cr, + foreground_pattern, + &foreground_source_used); + cairo_pattern_destroy (foreground_pattern); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_status (cr); + } + + cairo_destroy (cr); + + if (status) { + cairo_surface_destroy (recording_surface); + scaled_glyph->color_glyph = FALSE; + scaled_glyph->color_glyph_set = TRUE; + return status; + } + + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface, + foreground_source_used ? foreground_color : NULL); + + scaled_glyph->color_glyph = TRUE; + scaled_glyph->color_glyph_set = TRUE; + + /* get metrics */ + + /* Copied from cairo-user-font.c */ + cairo_matrix_t extent_scale; + double extent_x_scale; + double extent_y_scale; + double snap_x_scale; + double snap_y_scale; + double fixed_scale, x_scale, y_scale; + + extent_scale = scaled_font->base.scale_inverse; + snap_x_scale = 1.0; + snap_y_scale = 1.0; + status = _cairo_matrix_compute_basis_scale_factors (&extent_scale, + &x_scale, &y_scale, + 1); + if (status == CAIRO_STATUS_SUCCESS) { + if (x_scale == 0) + x_scale = 1; + if (y_scale == 0) + y_scale = 1; + + snap_x_scale = x_scale; + snap_y_scale = y_scale; + + /* since glyphs are pretty much 1.0x1.0, we can reduce error by + * scaling to a larger square. say, 1024.x1024. */ + fixed_scale = 1024; + x_scale /= fixed_scale; + y_scale /= fixed_scale; + + cairo_matrix_scale (&extent_scale, 1.0 / x_scale, 1.0 / y_scale); + + extent_x_scale = x_scale; + extent_y_scale = y_scale; + } + + { + /* compute width / height */ + cairo_box_t bbox; + double x1, y1, x2, y2; + double x_scale, y_scale; + + /* Compute extents.x/y/width/height from recording_surface, + * in font space. + */ + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + &extent_scale); + if (unlikely (status)) + return status; + + _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); + + x_scale = extent_x_scale; + y_scale = extent_y_scale; + extents->x_bearing = x1 * x_scale; + extents->y_bearing = y1 * y_scale; + extents->width = (x2 - x1) * x_scale; + extents->height = (y2 - y1) * y_scale; + } + + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + extents->x_advance = _cairo_lround (extents->x_advance / snap_x_scale) * snap_x_scale; + extents->y_advance = _cairo_lround (extents->y_advance / snap_y_scale) * snap_y_scale; + } + + return status; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_int_status_t +_cairo_ft_scaled_glyph_init_record_svg_glyph (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + const cairo_color_t *foreground_color, + cairo_text_extents_t *extents) +{ +#if HAVE_FT_SVG_DOCUMENT + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_surface_t *recording_surface; + cairo_t *cr; + FT_SVG_Document svg_doc = face->glyph->other; + char *svg_document; + FT_Color *palette; + unsigned int num_palette_entries; + cairo_bool_t foreground_source_used = FALSE; + + /* Create NULL terminated SVG document */ + svg_document = _cairo_strndup ((const char*)svg_doc->svg_document, svg_doc->svg_document_length); + + recording_surface = + cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + + cr = cairo_create (recording_surface); + + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cairo_matrix_t scale; + scale = scaled_font->base.scale; + scale.x0 = scale.y0 = 0.; + cairo_set_matrix (cr, &scale); + } + + cairo_set_font_size (cr, 1.0); + cairo_set_font_options (cr, &scaled_font->base.options); + + extents->x_bearing = DOUBLE_FROM_26_6(face->bbox.xMin); + extents->y_bearing = DOUBLE_FROM_26_6(face->bbox.yMin); + extents->width = DOUBLE_FROM_26_6(face->bbox.xMax) - extents->x_bearing; + extents->height = DOUBLE_FROM_26_6(face->bbox.yMax) - extents->y_bearing; + + _cairo_ft_scaled_glyph_set_palette (scaled_font, face, &num_palette_entries, &palette); + + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cairo_pattern_t *foreground_pattern = _cairo_pattern_create_solid (foreground_color); + status = _cairo_render_svg_glyph (svg_document, + svg_doc->start_glyph_id, + svg_doc->end_glyph_id, + _cairo_scaled_glyph_index(scaled_glyph), + svg_doc->units_per_EM, + palette, + num_palette_entries, + cr, + foreground_pattern, + &foreground_source_used); + cairo_pattern_destroy (foreground_pattern); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_status (cr); + } + + cairo_destroy (cr); + free (svg_document); + + if (status) { + cairo_surface_destroy (recording_surface); + scaled_glyph->color_glyph = FALSE; + scaled_glyph->color_glyph_set = TRUE; + return status; + } + + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface, + foreground_source_used ? foreground_color : NULL); + + scaled_glyph->color_glyph = TRUE; + scaled_glyph->color_glyph_set = TRUE; + + /* get metrics */ + + /* Copied from cairo-user-font.c */ + cairo_matrix_t extent_scale; + double extent_x_scale; + double extent_y_scale; + double snap_x_scale; + double snap_y_scale; + double fixed_scale, x_scale, y_scale; + + extent_scale = scaled_font->base.scale_inverse; + snap_x_scale = 1.0; + snap_y_scale = 1.0; + status = _cairo_matrix_compute_basis_scale_factors (&extent_scale, + &x_scale, &y_scale, + 1); + if (status == CAIRO_STATUS_SUCCESS) { + if (x_scale == 0) + x_scale = 1; + if (y_scale == 0) + y_scale = 1; + + snap_x_scale = x_scale; + snap_y_scale = y_scale; + + /* since glyphs are pretty much 1.0x1.0, we can reduce error by + * scaling to a larger square. say, 1024.x1024. */ + fixed_scale = 1024; + x_scale /= fixed_scale; + y_scale /= fixed_scale; + + cairo_matrix_scale (&extent_scale, 1.0 / x_scale, 1.0 / y_scale); + + extent_x_scale = x_scale; + extent_y_scale = y_scale; + } + + { + /* compute width / height */ + cairo_box_t bbox; + double x1, y1, x2, y2; + double x_scale, y_scale; + + /* Compute extents.x/y/width/height from recording_surface, + * in font space. + */ + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + &extent_scale); + if (unlikely (status)) + return status; + + _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); + + x_scale = extent_x_scale; + y_scale = extent_y_scale; + extents->x_bearing = x1 * x_scale; + extents->y_bearing = y1 * y_scale; + extents->width = (x2 - x1) * x_scale; + extents->height = (y2 - y1) * y_scale; + } + + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + extents->x_advance = _cairo_lround (extents->x_advance / snap_x_scale) * snap_x_scale; + extents->y_advance = _cairo_lround (extents->y_advance / snap_y_scale) * snap_y_scale; + } + + return status; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_int_status_t +_cairo_ft_scaled_glyph_init_surface_for_recording_surface (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + const cairo_color_t *foreground_color) +{ + cairo_surface_t *surface; + int width, height; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_bool_t foreground_used; + + width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + + cairo_surface_set_device_offset (surface, + - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), + - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); + + status = _cairo_recording_surface_replay_with_foreground_color (scaled_glyph->recording_surface, + surface, + foreground_color, + &foreground_used); + if (unlikely (status)) { + cairo_surface_destroy(surface); + return status; + } + + _cairo_scaled_glyph_set_color_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *)surface, + foreground_used ? foreground_color : NULL); + surface = NULL; + + if (surface) + cairo_surface_destroy (surface); + + return status; +} + +static void +_cairo_ft_scaled_glyph_get_metrics (cairo_ft_scaled_font_t *scaled_font, + FT_Face face, + cairo_bool_t vertical_layout, + int load_flags, + cairo_text_extents_t *fs_metrics) +{ + FT_Glyph_Metrics *metrics; + double x_factor, y_factor; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; + FT_GlyphSlot glyph = face->glyph; + + /* + * Compute font-space metrics + */ + metrics = &glyph->metrics; + + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + /* + * Note: Y coordinates of the horizontal bearing need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + * + * If we want hinted metrics but aren't asking for hinted glyphs from + * FreeType, then we need to do the metric hinting ourselves. + */ + + if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING)) + { + FT_Pos x1, x2; + FT_Pos y1, y2; + FT_Pos advance; + + if (!vertical_layout) { + x1 = (metrics->horiBearingX) & -64; + x2 = (metrics->horiBearingX + metrics->width + 63) & -64; + y1 = (-metrics->horiBearingY) & -64; + y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; + + advance = ((metrics->horiAdvance + 32) & -64); + + fs_metrics->x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics->y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics->width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics->height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics->x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; + fs_metrics->y_advance = 0; + } else { + x1 = (metrics->vertBearingX) & -64; + x2 = (metrics->vertBearingX + metrics->width + 63) & -64; + y1 = (metrics->vertBearingY) & -64; + y2 = (metrics->vertBearingY + metrics->height + 63) & -64; + + advance = ((metrics->vertAdvance + 32) & -64); + + fs_metrics->x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics->y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics->width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics->height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics->x_advance = 0; + fs_metrics->y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; + } + } else { + fs_metrics->width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; + fs_metrics->height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; + + if (!vertical_layout) { + fs_metrics->x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; + fs_metrics->y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; + + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) + fs_metrics->x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; + else + fs_metrics->x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor; + fs_metrics->y_advance = 0 * y_factor; + } else { + fs_metrics->x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; + fs_metrics->y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; + + fs_metrics->x_advance = 0 * x_factor; + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) + fs_metrics->y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; + else + fs_metrics->y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor; + } + } +} + +static cairo_bool_t +_cairo_ft_scaled_glyph_is_colr_v0 (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face) +{ +#ifdef HAVE_FT_PALETTE_SELECT + FT_LayerIterator iterator; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + + iterator.p = NULL; + if (FT_Get_Color_Glyph_Layer(face, + _cairo_scaled_glyph_index (scaled_glyph), + &layer_glyph_index, + &layer_color_index, + &iterator) == 1) + { + return TRUE; + } +#endif + return FALSE; +} + +static cairo_bool_t +_cairo_ft_scaled_glyph_is_colr_v1 (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face) +{ +#if HAVE_FT_COLR_V1 + FT_OpaquePaint paint = { NULL, 0 }; + + if (FT_Get_Color_Glyph_Paint (face, + _cairo_scaled_glyph_index (scaled_glyph), + FT_COLOR_INCLUDE_ROOT_TRANSFORM, + &paint) == 1) + { + return TRUE; + } +#endif + return FALSE; +} + +static const int ft_glyph_private_key; + +static cairo_int_status_t +_cairo_ft_scaled_glyph_init_metrics (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + cairo_bool_t vertical_layout, + int load_flags, + const cairo_color_t *foreground_color) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + cairo_text_extents_t fs_metrics; + cairo_ft_glyph_private_t *glyph_priv; + + cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; + + /* _cairo_ft_scaled_glyph_init_metrics() is called once the first + * time a cairo_scaled_glyph_t is created. We first allocate the + * cairo_ft_glyph_private_t struct and determine the glyph type. + */ + + glyph_priv = _cairo_malloc (sizeof (*glyph_priv)); + if (unlikely (glyph_priv == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_scaled_glyph_attach_private (scaled_glyph, &glyph_priv->base, + &ft_glyph_private_key, + _cairo_ft_glyph_fini); + scaled_glyph->dev_private = glyph_priv; + + /* We need to load color to determine if this is a color format. */ + int color_flag = 0; + +#ifdef FT_LOAD_COLOR + if (scaled_font->unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR) + color_flag = FT_LOAD_COLOR; +#endif + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags | color_flag, + !hint_metrics, + vertical_layout); + if (unlikely (status)) + return status; + + cairo_bool_t is_svg_format = FALSE; +#if HAVE_FT_SVG_DOCUMENT + if (face->glyph->format == FT_GLYPH_FORMAT_SVG) + is_svg_format = TRUE; +#endif + + if (is_svg_format) { + glyph_priv->format = CAIRO_FT_GLYPH_TYPE_SVG; + } else if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + glyph_priv->format = CAIRO_FT_GLYPH_TYPE_OUTLINE; + if (color_flag) { + if (_cairo_ft_scaled_glyph_is_colr_v1 (scaled_font, scaled_glyph, face)) + glyph_priv->format = CAIRO_FT_GLYPH_TYPE_COLR_V1; + else if (_cairo_ft_scaled_glyph_is_colr_v0 (scaled_font, scaled_glyph, face)) + glyph_priv->format = CAIRO_FT_GLYPH_TYPE_COLR_V0; + } + } else { + /* For anything else we let FreeType render a bitmap. */ + glyph_priv->format = CAIRO_FT_GLYPH_TYPE_BITMAP; + } + + _cairo_ft_scaled_glyph_get_metrics (scaled_font, + face, + vertical_layout, + load_flags, + &fs_metrics); + + + /* SVG and COLRv1 glyphs require the bounding box to be obtained + * from the ink extents of the rendering. We need to render glyph + * to a recording surface to obtain these extents. But we also + * need the advance from _cairo_ft_scaled_glyph_get_metrics() + * before calling this function. + */ + + if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG) { + status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_svg_glyph (scaled_font, + scaled_glyph, + face, + foreground_color, + &fs_metrics); + if (unlikely (status)) + return status; + } + + if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) { + if (!hint_metrics) { + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags | color_flag, + FALSE, + vertical_layout); + if (unlikely (status)) + return status; + } + + status = (cairo_int_status_t)_cairo_ft_scaled_glyph_init_record_colr_v1_glyph (scaled_font, + scaled_glyph, + face, + foreground_color, + &fs_metrics); + if (unlikely (status)) + return status; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &fs_metrics); + + return status; +} + +static cairo_int_status_t _cairo_ft_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info, const cairo_color_t *foreground_color) { - cairo_text_extents_t fs_metrics; cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; - FT_GlyphSlot glyph; FT_Face face; int load_flags = scaled_font->ft_options.load_flags; - FT_Glyph_Metrics *metrics; - double x_factor, y_factor; cairo_bool_t vertical_layout = FALSE; cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_bool_t scaled_glyph_loaded = FALSE; + cairo_ft_glyph_private_t *glyph_priv; + int color_flag = 0; + +#ifdef FT_LOAD_COLOR + color_flag = FT_LOAD_COLOR; +#endif face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) @@ -2660,134 +3437,77 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, vertical_layout = TRUE; } + /* Metrics will always be requested when a scaled glyph is created */ if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { - - cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; - - /* The font metrics for color glyphs should be the same as the - * outline glyphs. But just in case there aren't, request the - * color or outline metrics based on the font option and if - * the font has color. - */ - int color_flag = 0; -#ifdef FT_LOAD_COLOR - if (unscaled->have_color && scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR) - color_flag = FT_LOAD_COLOR; -#endif - status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, - scaled_glyph, - face, - load_flags | color_flag, - !hint_metrics, - vertical_layout); + status = _cairo_ft_scaled_glyph_init_metrics (scaled_font, + scaled_glyph, + face, + vertical_layout, + load_flags, + foreground_color); if (unlikely (status)) goto FAIL; + } - glyph = face->glyph; - scaled_glyph_loaded = hint_metrics; - - /* - * Compute font-space metrics - */ - metrics = &glyph->metrics; - - if (unscaled->x_scale == 0) - x_factor = 0; - else - x_factor = 1 / unscaled->x_scale; - - if (unscaled->y_scale == 0) - y_factor = 0; - else - y_factor = 1 / unscaled->y_scale; - - /* - * Note: Y coordinates of the horizontal bearing need to be negated. - * - * Scale metrics back to glyph space from the scaled glyph space returned - * by FreeType - * - * If we want hinted metrics but aren't asking for hinted glyphs from - * FreeType, then we need to do the metric hinting ourselves. - */ + /* scaled_glyph->dev_private is intialized by _cairo_ft_scaled_glyph_init_metrics() */ + glyph_priv = scaled_glyph->dev_private; + assert (glyph_priv != NULL); - if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING)) + if (info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG || + glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V0 || + glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) { - FT_Pos x1, x2; - FT_Pos y1, y2; - FT_Pos advance; - - if (!vertical_layout) { - x1 = (metrics->horiBearingX) & -64; - x2 = (metrics->horiBearingX + metrics->width + 63) & -64; - y1 = (-metrics->horiBearingY) & -64; - y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; - - advance = ((metrics->horiAdvance + 32) & -64); - - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; - - fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; - fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; - - fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; - fs_metrics.y_advance = 0; - } else { - x1 = (metrics->vertBearingX) & -64; - x2 = (metrics->vertBearingX + metrics->width + 63) & -64; - y1 = (metrics->vertBearingY) & -64; - y2 = (metrics->vertBearingY + metrics->height + 63) & -64; - - advance = ((metrics->vertAdvance + 32) & -64); - - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; - - fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; - fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; - - fs_metrics.x_advance = 0; - fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; - } - } else { - fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; - fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; - - if (!vertical_layout) { - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; - - if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) - fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; - else - fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor; - fs_metrics.y_advance = 0 * y_factor; - } else { - fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; - fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags | color_flag, + FALSE, + vertical_layout); + if (unlikely (status)) + goto FAIL; - fs_metrics.x_advance = 0 * x_factor; - if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) - fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; - else - fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor; + if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG) { + status = _cairo_ft_scaled_glyph_init_record_svg_glyph (scaled_font, + scaled_glyph, + face, + foreground_color, + &scaled_glyph->fs_metrics); + } else if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) { + status = _cairo_ft_scaled_glyph_init_record_colr_v1_glyph (scaled_font, + scaled_glyph, + face, + foreground_color, + &scaled_glyph->fs_metrics); + } else if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V0) { + status = _cairo_ft_scaled_glyph_init_record_colr_v0_glyph (scaled_font, + scaled_glyph, + face, + vertical_layout, + load_flags); } - } - - _cairo_scaled_glyph_set_metrics (scaled_glyph, - &scaled_font->base, - &fs_metrics); + } + if (status) + goto FAIL; } if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) { - status = _cairo_ft_scaled_glyph_init_surface (scaled_font, - scaled_glyph, - CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, - face, - foreground_color, - vertical_layout, - load_flags); + if (glyph_priv->format == CAIRO_FT_GLYPH_TYPE_SVG || + glyph_priv->format == CAIRO_FT_GLYPH_TYPE_COLR_V1) + { + status = _cairo_ft_scaled_glyph_init_surface_for_recording_surface (scaled_font, + scaled_glyph, + foreground_color); + } else { + status = _cairo_ft_scaled_glyph_init_surface (scaled_font, + scaled_glyph, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + face, + foreground_color, + vertical_layout, + load_flags); + } if (unlikely (status)) goto FAIL; } @@ -2807,31 +3527,18 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { cairo_path_fixed_t *path = NULL; /* hide compiler warning */ - /* - * A kludge -- the above code will trash the outline, - * so reload it. This will probably never occur though - */ - if ((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) { - scaled_glyph_loaded = FALSE; - load_flags |= FT_LOAD_NO_BITMAP; - } - - if (!scaled_glyph_loaded) { - status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, - scaled_glyph, - face, - load_flags, - FALSE, - vertical_layout); - if (unlikely (status)) - goto FAIL; - - glyph = face->glyph; - } + /* Load non-color glyph */ + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags, + FALSE, + vertical_layout); + if (unlikely (status)) + goto FAIL; - if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) - status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, - &path); + if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) + status = _cairo_ft_face_decompose_glyph_outline (face, &path); else status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -2971,7 +3678,7 @@ _cairo_ft_is_synthetic (void *abstract_font, error = FT_Get_MM_Var (face, &mm_var); if (error) { - status = _cairo_error (_ft_to_cairo_error (error)); + status = _cairo_error (_cairo_ft_to_cairo_error (error)); goto cleanup; } diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h index 0dc811472..836f7e523 100644 --- a/src/cairo-ft-private.h +++ b/src/cairo-ft-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc @@ -55,6 +56,41 @@ _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font); cairo_private unsigned int _cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); +cairo_private cairo_status_t +_cairo_ft_to_cairo_error (FT_Error error); + +cairo_private cairo_status_t +_cairo_ft_face_decompose_glyph_outline (FT_Face face, + cairo_path_fixed_t **pathp); + +#if HAVE_FT_SVG_DOCUMENT + +typedef struct FT_Color_ FT_Color; + +cairo_private cairo_status_t +_cairo_render_svg_glyph (const char *svg_document, + unsigned long first_glyph, + unsigned long last_glyph, + unsigned long glyph, + double units_per_em, + FT_Color *palette, + int num_palette_entries, + cairo_t *cr, + cairo_pattern_t *foreground_source, + cairo_bool_t *foreground_source_used); +#endif + +#if HAVE_FT_COLR_V1 +cairo_private cairo_status_t +_cairo_render_colr_v1_glyph (FT_Face face, + unsigned long glyph, + FT_Color *palette, + int num_palette_entries, + cairo_t *cr, + cairo_pattern_t *foreground_source, + cairo_bool_t *foreground_source_used); +#endif + CAIRO_END_DECLS #endif /* CAIRO_HAS_FT_FONT */ diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c deleted file mode 100644 index 5477ef0d8..000000000 --- a/src/cairo-gl-composite.c +++ /dev/null @@ -1,1364 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 Red Hat, Inc - * Copyright © 2011 Linaro Limited - * Copyright © 2011 Samsung Electronics - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - * Henry Song <hsong@sisa.samsung.com> - * Martin Robinson <mrobinson@igalia.com> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-clip-private.h" -#include "cairo-error-private.h" -#include "cairo-image-surface-private.h" - -/* FIXME: Copy of same routine in cairo-gl-msaa-compositor.c */ -static cairo_int_status_t -_draw_int_rect (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_rectangle_int_t *rect) -{ - cairo_box_t box; - cairo_point_t quad[4]; - - _cairo_box_from_rectangle (&box, rect); - quad[0].x = box.p1.x; - quad[0].y = box.p1.y; - quad[1].x = box.p1.x; - quad[1].y = box.p2.y; - quad[2].x = box.p2.x; - quad[2].y = box.p2.y; - quad[3].x = box.p2.x; - quad[3].y = box.p1.y; - - return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); -} - -static cairo_int_status_t -_blit_texture_to_renderbuffer (cairo_gl_surface_t *surface) -{ - cairo_gl_context_t *ctx = NULL; - cairo_gl_composite_t setup; - cairo_surface_pattern_t pattern; - cairo_rectangle_int_t extents; - cairo_int_status_t status; - - /* FIXME: This only permits blit when glesv3 is enabled. But note that - glesv2 with the ANGLE extension should also be able to support this feature, - so once the ANGLE support code is in place this check can be relaxed. */ - if (((cairo_gl_context_t *)surface->base.device)->gl_flavor != CAIRO_GL_FLAVOR_ES3) - return CAIRO_INT_STATUS_SUCCESS; - - if (! surface->content_in_texture) - return CAIRO_INT_STATUS_SUCCESS; - - memset (&setup, 0, sizeof (cairo_gl_composite_t)); - - status = _cairo_gl_composite_set_operator (&setup, - CAIRO_OPERATOR_SOURCE, - FALSE); - - if (status) - return status; - - setup.dst = surface; - setup.clip_region = surface->clip_region; - - _cairo_pattern_init_for_surface (&pattern, &surface->base); - status = _cairo_gl_composite_set_source (&setup, &pattern.base, - NULL, NULL, FALSE); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_multisample (&setup); - - status = _cairo_gl_composite_begin (&setup, &ctx); - - if (unlikely (status)) - goto FAIL; - - extents.x = extents.y = 0; - extents.width = surface->width; - extents.height = surface->height; - - status = _draw_int_rect (ctx, &setup, &extents); - - if (status == CAIRO_INT_STATUS_SUCCESS) - surface->content_in_texture = FALSE; - -FAIL: - _cairo_gl_composite_fini (&setup); - - if (ctx) { - _cairo_gl_composite_flush (ctx); - status = _cairo_gl_context_release (ctx, status); - } - - return status; -} - -cairo_int_status_t -_cairo_gl_composite_set_source (cairo_gl_composite_t *setup, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen) -{ - _cairo_gl_operand_destroy (&setup->src); - return _cairo_gl_operand_init (&setup->src, pattern, setup->dst, - sample, extents, use_texgen); -} - -void -_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, - const cairo_gl_operand_t *source) -{ - cairo_int_status_t status; - - _cairo_gl_operand_destroy (&setup->src); - _cairo_gl_operand_copy (&setup->src, source); - - if (source->type == CAIRO_GL_OPERAND_TEXTURE) - status = _cairo_gl_surface_resolve_multisampling (source->texture.surface); -} - -void -_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup, - const cairo_color_t *color) -{ - _cairo_gl_operand_destroy (&setup->src); - _cairo_gl_solid_operand_init (&setup->src, color); -} - -cairo_int_status_t -_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen) -{ - _cairo_gl_operand_destroy (&setup->mask); - if (pattern == NULL) - return CAIRO_STATUS_SUCCESS; - - return _cairo_gl_operand_init (&setup->mask, pattern, setup->dst, - sample, extents, use_texgen); -} - -void -_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup, - const cairo_gl_operand_t *mask) -{ - cairo_int_status_t status; - _cairo_gl_operand_destroy (&setup->mask); - if (mask) { - _cairo_gl_operand_copy (&setup->mask, mask); - if (mask->type == CAIRO_GL_OPERAND_TEXTURE) - status = _cairo_gl_surface_resolve_multisampling (mask->texture.surface); - } -} - -void -_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup) -{ - setup->spans = TRUE; -} - -void -_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup) -{ - setup->multisample = TRUE; -} - -void -_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, - cairo_region_t *clip_region) -{ - setup->clip_region = clip_region; -} - -void -_cairo_gl_composite_set_clip (cairo_gl_composite_t *setup, - cairo_clip_t *clip) -{ - setup->clip = clip; -} - -static void -_cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup) -{ - _cairo_gl_shader_bind_matrix4f(ctx, ctx->current_shader->mvp_location, - ctx->modelviewprojection_matrix); - _cairo_gl_operand_bind_to_shader (ctx, &setup->src, CAIRO_GL_TEX_SOURCE); - _cairo_gl_operand_bind_to_shader (ctx, &setup->mask, CAIRO_GL_TEX_MASK); -} - -static void -_cairo_gl_texture_set_filter (cairo_gl_context_t *ctx, - GLuint target, - cairo_filter_t filter) -{ - switch (filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; - default: - case CAIRO_FILTER_GAUSSIAN: - ASSERT_NOT_REACHED; - } -} - -static void -_cairo_gl_texture_set_extend (cairo_gl_context_t *ctx, - GLuint target, - cairo_extend_t extend) -{ - GLint wrap_mode; - assert (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base) || - (extend != CAIRO_EXTEND_REPEAT && extend != CAIRO_EXTEND_REFLECT)); - - switch (extend) { - case CAIRO_EXTEND_NONE: - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) - wrap_mode = GL_CLAMP_TO_EDGE; - else - wrap_mode = GL_CLAMP_TO_BORDER; - break; - case CAIRO_EXTEND_PAD: - wrap_mode = GL_CLAMP_TO_EDGE; - break; - case CAIRO_EXTEND_REPEAT: - if (ctx->has_npot_repeat) - wrap_mode = GL_REPEAT; - else - wrap_mode = GL_CLAMP_TO_EDGE; - break; - case CAIRO_EXTEND_REFLECT: - if (ctx->has_npot_repeat) - wrap_mode = GL_MIRRORED_REPEAT; - else - wrap_mode = GL_CLAMP_TO_EDGE; - break; - default: - wrap_mode = 0; - } - - if (likely (wrap_mode)) { - glTexParameteri (target, GL_TEXTURE_WRAP_S, wrap_mode); - glTexParameteri (target, GL_TEXTURE_WRAP_T, wrap_mode); - } -} - - -static void -_cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit, - cairo_gl_operand_t *operand, - unsigned int vertex_offset, - cairo_bool_t vertex_size_changed) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - cairo_bool_t needs_setup; - - /* XXX: we need to do setup when switching from shaders - * to no shaders (or back) */ - needs_setup = vertex_size_changed; - needs_setup |= _cairo_gl_operand_needs_setup (&ctx->operands[tex_unit], - operand, - vertex_offset); - - if (needs_setup) { - _cairo_gl_composite_flush (ctx); - _cairo_gl_context_destroy_operand (ctx, tex_unit); - } - - memcpy (&ctx->operands[tex_unit], operand, sizeof (cairo_gl_operand_t)); - ctx->operands[tex_unit].vertex_offset = vertex_offset; - - if (! needs_setup) - return; - - switch (operand->type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - break; - /* fall through */ - case CAIRO_GL_OPERAND_CONSTANT: - break; - case CAIRO_GL_OPERAND_TEXTURE: - glActiveTexture (GL_TEXTURE0 + tex_unit); - glBindTexture (ctx->tex_target, operand->texture.tex); - _cairo_gl_texture_set_extend (ctx, ctx->tex_target, - operand->texture.attributes.extend); - _cairo_gl_texture_set_filter (ctx, ctx->tex_target, - operand->texture.attributes.filter); - - if (! operand->texture.texgen) { - dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2, - GL_FLOAT, GL_FALSE, ctx->vertex_size, - ctx->vb + vertex_offset); - dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); - } - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - glActiveTexture (GL_TEXTURE0 + tex_unit); - glBindTexture (ctx->tex_target, operand->gradient.gradient->tex); - _cairo_gl_texture_set_extend (ctx, ctx->tex_target, operand->gradient.extend); - _cairo_gl_texture_set_filter (ctx, ctx->tex_target, CAIRO_FILTER_BILINEAR); - - if (! operand->gradient.texgen) { - dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2, - GL_FLOAT, GL_FALSE, ctx->vertex_size, - ctx->vb + vertex_offset); - dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); - } - break; - } -} - -static void -_cairo_gl_context_setup_spans (cairo_gl_context_t *ctx, - cairo_bool_t spans_enabled, - unsigned int vertex_size, - unsigned int vertex_offset) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - - if (! spans_enabled) { - dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); - ctx->spans = FALSE; - return; - } - - dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4, - GL_UNSIGNED_BYTE, GL_TRUE, vertex_size, - ctx->vb + vertex_offset); - dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); - ctx->spans = TRUE; -} - -void -_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - - if (!_cairo_gl_context_is_flushed (ctx)) - _cairo_gl_composite_flush (ctx); - - switch (ctx->operands[tex_unit].type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - break; - /* fall through */ - case CAIRO_GL_OPERAND_CONSTANT: - break; - case CAIRO_GL_OPERAND_TEXTURE: - dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); - break; - } - - memset (&ctx->operands[tex_unit], 0, sizeof (cairo_gl_operand_t)); -} - -static void -_cairo_gl_set_operator (cairo_gl_context_t *ctx, - cairo_operator_t op, - cairo_bool_t component_alpha) -{ - struct { - GLenum src; - GLenum dst; - } blend_factors[] = { - { GL_ZERO, GL_ZERO }, /* Clear */ - { GL_ONE, GL_ZERO }, /* Source */ - { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */ - { GL_DST_ALPHA, GL_ZERO }, /* In */ - { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */ - { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */ - - { GL_ZERO, GL_ONE }, /* Dest */ - { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */ - { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */ - { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */ - { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */ - - { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */ - { GL_ONE, GL_ONE }, /* Add */ - }; - GLenum src_factor, dst_factor; - - assert (op < ARRAY_LENGTH (blend_factors)); - /* different dst and component_alpha changes cause flushes elsewhere */ - if (ctx->current_operator != op) - _cairo_gl_composite_flush (ctx); - ctx->current_operator = op; - - src_factor = blend_factors[op].src; - dst_factor = blend_factors[op].dst; - - /* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA - * due to texture filtering of GL_CLAMP_TO_BORDER. So fix those - * bits in that case. - */ - if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) { - if (src_factor == GL_ONE_MINUS_DST_ALPHA) - src_factor = GL_ZERO; - if (src_factor == GL_DST_ALPHA) - src_factor = GL_ONE; - } - - if (component_alpha) { - if (dst_factor == GL_ONE_MINUS_SRC_ALPHA) - dst_factor = GL_ONE_MINUS_SRC_COLOR; - if (dst_factor == GL_SRC_ALPHA) - dst_factor = GL_SRC_COLOR; - } - - if (ctx->current_target->base.content == CAIRO_CONTENT_ALPHA) { - glBlendFuncSeparate (GL_ZERO, GL_ZERO, src_factor, dst_factor); - } else if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) { - glBlendFuncSeparate (src_factor, dst_factor, GL_ONE, GL_ONE); - } else { - glBlendFunc (src_factor, dst_factor); - } -} - -static cairo_status_t -_cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup) -{ - cairo_gl_shader_t *pre_shader = NULL; - cairo_status_t status; - - /* For CLEAR, cairo's rendering equation (quoting Owen's description in: - * https://lists.cairographics.org/archives/cairo/2005-August/004992.html) - * is: - * mask IN clip ? src OP dest : dest - * or more simply: - * mask IN CLIP ? 0 : dest - * - * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C). - * - * The model we use in _cairo_gl_set_operator() is Render's: - * src IN mask IN clip OP dest - * which would boil down to: - * 0 (bounded by the extents of the drawing). - * - * However, we can do a Render operation using an opaque source - * and DEST_OUT to produce: - * 1 IN mask IN clip DEST_OUT dest - * which is - * mask IN clip ? 0 : dest - */ - if (setup->op == CAIRO_OPERATOR_CLEAR) { - _cairo_gl_solid_operand_init (&setup->src, CAIRO_COLOR_WHITE); - setup->op = CAIRO_OPERATOR_DEST_OUT; - } - - /* - * implements component-alpha %CAIRO_OPERATOR_OVER using two passes of - * the simpler operations %CAIRO_OPERATOR_DEST_OUT and %CAIRO_OPERATOR_ADD. - * - * From http://anholt.livejournal.com/32058.html: - * - * The trouble is that component-alpha rendering requires two different sources - * for blending: one for the source value to the blender, which is the - * per-channel multiplication of source and mask, and one for the source alpha - * for multiplying with the destination channels, which is the multiplication - * of the source channels by the mask alpha. So the equation for Over is: - * - * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A - * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R - * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G - * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B - * - * But we can do some simpler operations, right? How about PictOpOutReverse, - * which has a source factor of 0 and dest factor of (1 - source alpha). We - * can get the source alpha value (srca.X = src.A * mask.X) out of the texture - * blenders pretty easily. So we can do a component-alpha OutReverse, which - * gets us: - * - * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A - * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R - * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G - * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B - * - * OK. And if an op doesn't use the source alpha value for the destination - * factor, then we can do the channel multiplication in the texture blenders - * to get the source value, and ignore the source alpha that we wouldn't use. - * We've supported this in the Radeon driver for a long time. An example would - * be PictOpAdd, which does: - * - * dst.A = src.A * mask.A + dst.A - * dst.R = src.R * mask.R + dst.R - * dst.G = src.G * mask.G + dst.G - * dst.B = src.B * mask.B + dst.B - * - * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right - * after it, we get: - * - * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) - * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) - * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) - * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) - * - * This two-pass trickery could be avoided using a new GL extension that - * lets two values come out of the shader and into the blend unit. - */ - if (setup->op == CAIRO_OPERATOR_OVER) { - setup->op = CAIRO_OPERATOR_ADD; - status = _cairo_gl_get_shader_by_type (ctx, - &setup->src, - &setup->mask, - setup->spans, - CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, - &pre_shader); - if (unlikely (status)) - return status; - } - - if (ctx->pre_shader != pre_shader) - _cairo_gl_composite_flush (ctx); - ctx->pre_shader = pre_shader; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_scissor_to_doubles (cairo_gl_surface_t *surface, - double x1, double y1, - double x2, double y2) -{ - double height; - - height = y2 - y1; - if (_cairo_gl_surface_is_texture (surface) == FALSE) - y1 = surface->height - (y1 + height); - glScissor (x1, y1, x2 - x1, height); - glEnable (GL_SCISSOR_TEST); -} - -void -_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface, - const cairo_rectangle_int_t *r) -{ - _scissor_to_doubles (surface, r->x, r->y, r->x+r->width, r->y+r->height); -} - -static void -_scissor_to_box (cairo_gl_surface_t *surface, - const cairo_box_t *box) -{ - double x1, y1, x2, y2; - _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2); - _scissor_to_doubles (surface, x1, y1, x2, y2); -} - -static cairo_bool_t -_cairo_gl_composite_setup_vbo (cairo_gl_context_t *ctx, - unsigned int size_per_vertex) -{ - cairo_bool_t vertex_size_changed = ctx->vertex_size != size_per_vertex; - if (vertex_size_changed) { - ctx->vertex_size = size_per_vertex; - _cairo_gl_composite_flush (ctx); - } - - if (_cairo_gl_context_is_flushed (ctx)) { - ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2, - GL_FLOAT, GL_FALSE, size_per_vertex, - ctx->vb); - ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX); - } - - return vertex_size_changed; -} - -static void -_disable_stencil_buffer (void) -{ - glDisable (GL_STENCIL_TEST); - glDepthMask (GL_FALSE); -} - -static cairo_int_status_t -_cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup, - cairo_gl_context_t *ctx, - int vertex_size) -{ - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; - - cairo_gl_surface_t *dst = setup->dst; - cairo_clip_t *clip = setup->clip; - - if (clip->num_boxes == 1 && clip->path == NULL) { - _scissor_to_box (dst, &clip->boxes[0]); - goto disable_stencil_buffer_and_return; - } - - if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto disable_stencil_buffer_and_return; - } - - /* We only want to clear the part of the stencil buffer - * that we are about to use. It also does not hurt to - * scissor around the painted clip. */ - _cairo_gl_scissor_to_rectangle (dst, _cairo_clip_get_extents (clip)); - - /* The clip is not rectangular, so use the stencil buffer. */ - glDepthMask (GL_TRUE); - glEnable (GL_STENCIL_TEST); - - /* Texture surfaces have private depth/stencil buffers, so we can - * rely on any previous clip being cached there. */ - if (_cairo_gl_surface_is_texture (setup->dst)) { - cairo_clip_t *old_clip = setup->dst->clip_on_stencil_buffer; - if (_cairo_clip_equal (old_clip, setup->clip)) - goto activate_stencil_buffer_and_return; - - if (old_clip) { - _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); - } - - setup->dst->clip_on_stencil_buffer = _cairo_clip_copy (setup->clip); - } - - glClearStencil (0); - glClear (GL_STENCIL_BUFFER_BIT); - - glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); - glStencilFunc (GL_EQUAL, 1, 0xffffffff); - glColorMask (0, 0, 0, 0); - - status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip); - - if (unlikely (status)) { - glColorMask (1, 1, 1, 1); - goto disable_stencil_buffer_and_return; - } - - /* We want to only render to the stencil buffer, so draw everything now. - Flushing also unbinds the VBO, which we want to rebind for regular - drawing. */ - _cairo_gl_composite_flush (ctx); - _cairo_gl_composite_setup_vbo (ctx, vertex_size); - -activate_stencil_buffer_and_return: - glColorMask (1, 1, 1, 1); - glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc (GL_EQUAL, 1, 0xffffffff); - return CAIRO_INT_STATUS_SUCCESS; - -disable_stencil_buffer_and_return: - _disable_stencil_buffer (); - return status; -} - -static cairo_int_status_t -_cairo_gl_composite_setup_clipping (cairo_gl_composite_t *setup, - cairo_gl_context_t *ctx, - int vertex_size) -{ - cairo_bool_t clip_changing = TRUE; - cairo_bool_t clip_region_changing = TRUE; - - if (! ctx->clip && ! setup->clip && ! setup->clip_region && ! ctx->clip_region) - goto disable_all_clipping; - - clip_changing = ! _cairo_clip_equal (ctx->clip, setup->clip); - clip_region_changing = ! cairo_region_equal (ctx->clip_region, setup->clip_region); - if (! _cairo_gl_context_is_flushed (ctx) && - (clip_region_changing || clip_changing)) - _cairo_gl_composite_flush (ctx); - - assert (!setup->clip_region || !setup->clip); - - /* setup->clip is only used by the msaa compositor and setup->clip_region - * only by the other compositors, so it's safe to wait to clean up obsolete - * clips. */ - if (clip_region_changing) { - cairo_region_destroy (ctx->clip_region); - ctx->clip_region = cairo_region_reference (setup->clip_region); - } - if (clip_changing) { - _cairo_clip_destroy (ctx->clip); - ctx->clip = _cairo_clip_copy (setup->clip); - } - - /* For clip regions, we scissor right before drawing. */ - if (setup->clip_region) - goto disable_all_clipping; - - if (setup->clip) - return _cairo_gl_composite_setup_painted_clipping (setup, ctx, - vertex_size); -disable_all_clipping: - _disable_stencil_buffer (); - glDisable (GL_SCISSOR_TEST); - return CAIRO_INT_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup, - cairo_gl_context_t *ctx) -{ - unsigned int dst_size, src_size, mask_size, vertex_size; - cairo_status_t status; - cairo_gl_shader_t *shader; - cairo_bool_t component_alpha; - cairo_bool_t vertex_size_changed; - - component_alpha = - setup->mask.type == CAIRO_GL_OPERAND_TEXTURE && - setup->mask.texture.attributes.has_component_alpha; - - /* Do various magic for component alpha */ - if (component_alpha) { - status = _cairo_gl_composite_begin_component_alpha (ctx, setup); - if (unlikely (status)) - return status; - } else { - if (ctx->pre_shader) { - _cairo_gl_composite_flush (ctx); - ctx->pre_shader = NULL; - } - } - - status = _cairo_gl_get_shader_by_type (ctx, - &setup->src, - &setup->mask, - setup->spans, - component_alpha ? - CAIRO_GL_SHADER_IN_CA_SOURCE : - CAIRO_GL_SHADER_IN_NORMAL, - &shader); - if (unlikely (status)) { - ctx->pre_shader = NULL; - return status; - } - if (ctx->current_shader != shader) - _cairo_gl_composite_flush (ctx); - - status = CAIRO_STATUS_SUCCESS; - - dst_size = 2 * sizeof (GLfloat); - src_size = _cairo_gl_operand_get_vertex_size (&setup->src); - mask_size = _cairo_gl_operand_get_vertex_size (&setup->mask); - vertex_size = dst_size + src_size + mask_size; - - if (setup->spans) - vertex_size += sizeof (GLfloat); - - vertex_size_changed = _cairo_gl_composite_setup_vbo (ctx, vertex_size); - - _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, dst_size, vertex_size_changed); - _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, dst_size + src_size, vertex_size_changed); - - _cairo_gl_context_setup_spans (ctx, setup->spans, vertex_size, - dst_size + src_size + mask_size); - - _cairo_gl_set_operator (ctx, setup->op, component_alpha); - - if (_cairo_gl_context_is_flushed (ctx)) { - if (ctx->pre_shader) { - _cairo_gl_set_shader (ctx, ctx->pre_shader); - _cairo_gl_composite_bind_to_shader (ctx, setup); - } - _cairo_gl_set_shader (ctx, shader); - _cairo_gl_composite_bind_to_shader (ctx, setup); - } - - return status; -} - -cairo_status_t -_cairo_gl_composite_begin (cairo_gl_composite_t *setup, - cairo_gl_context_t **ctx_out) -{ - cairo_gl_context_t *ctx; - cairo_status_t status; - - assert (setup->dst); - - status = _cairo_gl_context_acquire (setup->dst->base.device, &ctx); - if (unlikely (status)) - return status; - - _cairo_gl_context_set_destination (ctx, setup->dst, setup->multisample); - glEnable (GL_BLEND); - - status = _cairo_gl_set_operands_and_operator (setup, ctx); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_setup_clipping (setup, ctx, ctx->vertex_size); - if (unlikely (status)) - goto FAIL; - - *ctx_out = ctx; - -FAIL: - if (unlikely (status)) - status = _cairo_gl_context_release (ctx, status); - - return status; -} - -static inline void -_cairo_gl_composite_draw_tristrip (cairo_gl_context_t *ctx) -{ - cairo_array_t* indices = &ctx->tristrip_indices; - const unsigned short *indices_array = _cairo_array_index_const (indices, 0); - - if (ctx->pre_shader) { - cairo_gl_shader_t *prev_shader = ctx->current_shader; - - _cairo_gl_set_shader (ctx, ctx->pre_shader); - _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE); - glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array); - - _cairo_gl_set_shader (ctx, prev_shader); - _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE); - } - - glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array); - _cairo_array_truncate (indices, 0); -} - -static inline void -_cairo_gl_composite_draw_triangles (cairo_gl_context_t *ctx, - unsigned int count) -{ - if (! ctx->pre_shader) { - glDrawArrays (GL_TRIANGLES, 0, count); - } else { - cairo_gl_shader_t *prev_shader = ctx->current_shader; - - _cairo_gl_set_shader (ctx, ctx->pre_shader); - _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE); - glDrawArrays (GL_TRIANGLES, 0, count); - - _cairo_gl_set_shader (ctx, prev_shader); - _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE); - glDrawArrays (GL_TRIANGLES, 0, count); - } -} - -static void -_cairo_gl_composite_draw_triangles_with_clip_region (cairo_gl_context_t *ctx, - unsigned int count) -{ - int i, num_rectangles; - - if (!ctx->clip_region) { - _cairo_gl_composite_draw_triangles (ctx, count); - return; - } - - num_rectangles = cairo_region_num_rectangles (ctx->clip_region); - for (i = 0; i < num_rectangles; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (ctx->clip_region, i, &rect); - - _cairo_gl_scissor_to_rectangle (ctx->current_target, &rect); - _cairo_gl_composite_draw_triangles (ctx, count); - } -} - -static void -_cairo_gl_composite_unmap_vertex_buffer (cairo_gl_context_t *ctx) -{ - ctx->vb_offset = 0; -} - -void -_cairo_gl_composite_flush (cairo_gl_context_t *ctx) -{ - unsigned int count; - int i; - - if (_cairo_gl_context_is_flushed (ctx)) - return; - - count = ctx->vb_offset / ctx->vertex_size; - _cairo_gl_composite_unmap_vertex_buffer (ctx); - - if (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS) { - _cairo_gl_composite_draw_tristrip (ctx); - } else { - assert (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); - _cairo_gl_composite_draw_triangles_with_clip_region (ctx, count); - } - - for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++) - _cairo_gl_glyph_cache_unlock (&ctx->glyph_cache[i]); -} - -static void -_cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx, - unsigned int n_vertices, - cairo_gl_primitive_type_t primitive_type) -{ - if (ctx->primitive_type != primitive_type) { - _cairo_gl_composite_flush (ctx); - ctx->primitive_type = primitive_type; - } - - assert(ctx->vbo_size > 0); - if (ctx->vb_offset + n_vertices * ctx->vertex_size > ctx->vbo_size) - _cairo_gl_composite_flush (ctx); -} - -static inline void -_cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx, - GLfloat x, GLfloat y) -{ - GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; - - *vb++ = x; - *vb++ = y; - - _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); - _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y); - - ctx->vb_offset += ctx->vertex_size; -} - -static inline void -_cairo_gl_composite_emit_alpha_vertex (cairo_gl_context_t *ctx, - GLfloat x, GLfloat y, uint8_t alpha) -{ - GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; - union fi { - float f; - GLbyte bytes[4]; - } fi; - - *vb++ = x; - *vb++ = y; - - _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); - _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y); - - fi.bytes[0] = 0; - fi.bytes[1] = 0; - fi.bytes[2] = 0; - fi.bytes[3] = alpha; - *vb++ = fi.f; - - ctx->vb_offset += ctx->vertex_size; -} - -static void -_cairo_gl_composite_emit_point (cairo_gl_context_t *ctx, - const cairo_point_t *point) -{ - _cairo_gl_composite_emit_vertex (ctx, - _cairo_fixed_to_double (point->x), - _cairo_fixed_to_double (point->y)); -} - -static void -_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2) -{ - _cairo_gl_composite_prepare_buffer (ctx, 6, - CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); - - _cairo_gl_composite_emit_vertex (ctx, x1, y1); - _cairo_gl_composite_emit_vertex (ctx, x2, y1); - _cairo_gl_composite_emit_vertex (ctx, x1, y2); - - _cairo_gl_composite_emit_vertex (ctx, x2, y1); - _cairo_gl_composite_emit_vertex (ctx, x2, y2); - _cairo_gl_composite_emit_vertex (ctx, x1, y2); -} - -cairo_gl_emit_rect_t -_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx) -{ - return _cairo_gl_composite_emit_rect; -} - -void -_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2) -{ - _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2); -} - -static void -_cairo_gl_composite_emit_span (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - uint8_t alpha) -{ - _cairo_gl_composite_prepare_buffer (ctx, 6, - CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); - - _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y1, alpha); - _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha); - _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha); - - _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha); - _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y2, alpha); - _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha); -} - -static void -_cairo_gl_composite_emit_solid_span (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - uint8_t alpha) -{ - GLfloat *v; - union fi { - float f; - GLbyte bytes[4]; - } fi; - - _cairo_gl_composite_prepare_buffer (ctx, 6, - CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); - v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; - - v[15] = v[ 6] = v[0] = x1; - v[10] = v[ 4] = v[1] = y1; - v[12] = v[ 9] = v[3] = x2; - v[16] = v[13] = v[7] = y2; - - fi.bytes[0] = 0; - fi.bytes[1] = 0; - fi.bytes[2] = 0; - fi.bytes[3] = alpha; - v[17] =v[14] = v[11] = v[8] = v[5] = v[2] = fi.f; - - ctx->vb_offset += 6*3 * sizeof(GLfloat); -} - -cairo_gl_emit_span_t -_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx) -{ - if (ctx->operands[CAIRO_GL_TEX_MASK].type != CAIRO_GL_OPERAND_NONE) { - switch (ctx->operands[CAIRO_GL_TEX_MASK].type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - break; - - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - if (!ctx->operands[CAIRO_GL_TEX_MASK].gradient.texgen) - return _cairo_gl_composite_emit_span; - break; - - case CAIRO_GL_OPERAND_TEXTURE: - if (!ctx->operands[CAIRO_GL_TEX_MASK].texture.texgen) - return _cairo_gl_composite_emit_span; - break; - } - } - - switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - break; - - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - if (!ctx->operands[CAIRO_GL_TEX_SOURCE].gradient.texgen) - return _cairo_gl_composite_emit_span; - break; - - case CAIRO_GL_OPERAND_TEXTURE: - if (!ctx->operands[CAIRO_GL_TEX_SOURCE].texture.texgen) - return _cairo_gl_composite_emit_span; - } - - return _cairo_gl_composite_emit_solid_span; -} - -static inline void -_cairo_gl_composite_emit_glyph_vertex (cairo_gl_context_t *ctx, - GLfloat x, GLfloat y, - GLfloat glyph_x, GLfloat glyph_y) -{ - GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; - - *vb++ = x; - *vb++ = y; - - _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); - - *vb++ = glyph_x; - *vb++ = glyph_y; - - ctx->vb_offset += ctx->vertex_size; -} - -static void -_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - GLfloat glyph_x1, GLfloat glyph_y1, - GLfloat glyph_x2, GLfloat glyph_y2) -{ - _cairo_gl_composite_prepare_buffer (ctx, 6, - CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); - - _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1); - _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1); - _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2); - - _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1); - _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2); - _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2); -} - -static void -_cairo_gl_composite_emit_solid_glyph (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - GLfloat glyph_x1, GLfloat glyph_y1, - GLfloat glyph_x2, GLfloat glyph_y2) -{ - GLfloat *v; - - _cairo_gl_composite_prepare_buffer (ctx, 6, - CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); - - v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; - - v[20] = v[ 8] = v[0] = x1; - v[13] = v[ 5] = v[1] = y1; - v[22] = v[10] = v[2] = glyph_x1; - v[15] = v[ 7] = v[3] = glyph_y1; - - v[16] = v[12] = v[4] = x2; - v[18] = v[14] = v[6] = glyph_x2; - - v[21] = v[17] = v[ 9] = y2; - v[23] = v[19] = v[11] = glyph_y2; - - ctx->vb_offset += 4 * 6 * sizeof (GLfloat); -} - -cairo_gl_emit_glyph_t -_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx) -{ - switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - return _cairo_gl_composite_emit_solid_glyph; - - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - case CAIRO_GL_OPERAND_TEXTURE: - return _cairo_gl_composite_emit_glyph; - } -} - -void -_cairo_gl_composite_fini (cairo_gl_composite_t *setup) -{ - _cairo_gl_operand_destroy (&setup->src); - _cairo_gl_operand_destroy (&setup->mask); -} - -cairo_status_t -_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup, - cairo_operator_t op, - cairo_bool_t assume_component_alpha) -{ - if (assume_component_alpha) { - if (op != CAIRO_OPERATOR_CLEAR && - op != CAIRO_OPERATOR_OVER && - op != CAIRO_OPERATOR_ADD) - return UNSUPPORTED ("unsupported component alpha operator"); - } else { - if (! _cairo_gl_operator_is_supported (op)) - return UNSUPPORTED ("unsupported operator"); - } - - setup->op = op; - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gl_composite_init (cairo_gl_composite_t *setup, - cairo_operator_t op, - cairo_gl_surface_t *dst, - cairo_bool_t assume_component_alpha) -{ - cairo_status_t status; - - status = _blit_texture_to_renderbuffer (dst); - - memset (setup, 0, sizeof (cairo_gl_composite_t)); - - status = _cairo_gl_composite_set_operator (setup, op, - assume_component_alpha); - if (status) - return status; - - setup->dst = dst; - setup->clip_region = dst->clip_region; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_gl_composite_append_vertex_indices (cairo_gl_context_t *ctx, - int number_of_new_indices) -{ - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; - cairo_array_t *indices = &ctx->tristrip_indices; - int number_of_indices = _cairo_array_num_elements (indices); - unsigned short current_vertex_index = 0; - int i; - - assert (number_of_new_indices > 0); - - /* If any preexisting triangle triangle strip indices exist on this - context, we insert a set of degenerate triangles from the last - preexisting vertex to our first one. */ - if (number_of_indices > 0) { - const unsigned short *indices_array = _cairo_array_index_const (indices, 0); - current_vertex_index = indices_array[number_of_indices - 1]; - - status = _cairo_array_append (indices, ¤t_vertex_index); - if (unlikely (status)) - return status; - - current_vertex_index++; - status =_cairo_array_append (indices, ¤t_vertex_index); - if (unlikely (status)) - return status; - } - - for (i = 0; i < number_of_new_indices; i++) { - status = _cairo_array_append (indices, ¤t_vertex_index); - current_vertex_index++; - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - const cairo_point_t quad[4]) -{ - _cairo_gl_composite_prepare_buffer (ctx, 4, - CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS); - - _cairo_gl_composite_emit_point (ctx, &quad[0]); - _cairo_gl_composite_emit_point (ctx, &quad[1]); - - /* Cairo stores quad vertices in counter-clockwise order, but we need to - emit them from top to bottom in the triangle strip, so we need to reverse - the order of the last two vertices. */ - _cairo_gl_composite_emit_point (ctx, &quad[3]); - _cairo_gl_composite_emit_point (ctx, &quad[2]); - - return _cairo_gl_composite_append_vertex_indices (ctx, 4); -} - -cairo_int_status_t -_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - const cairo_point_t triangle[3]) -{ - _cairo_gl_composite_prepare_buffer (ctx, 3, - CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS); - - _cairo_gl_composite_emit_point (ctx, &triangle[0]); - _cairo_gl_composite_emit_point (ctx, &triangle[1]); - _cairo_gl_composite_emit_point (ctx, &triangle[2]); - return _cairo_gl_composite_append_vertex_indices (ctx, 3); -} diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c deleted file mode 100644 index 6f4c852a4..000000000 --- a/src/cairo-gl-device.c +++ /dev/null @@ -1,851 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 Red Hat, Inc - * Copyright © 2010 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - */ - -#include "cairoint.h" - -#include "cairo-error-private.h" -#include "cairo-gl-private.h" - -#define MAX_MSAA_SAMPLES 4 - -static void -_gl_lock (void *device) -{ - cairo_gl_context_t *ctx = (cairo_gl_context_t *) device; - - ctx->acquire (ctx); -} - -static void -_gl_unlock (void *device) -{ - cairo_gl_context_t *ctx = (cairo_gl_context_t *) device; - - ctx->release (ctx); -} - -static cairo_status_t -_gl_flush (void *device) -{ - cairo_gl_context_t *ctx; - cairo_status_t status; - - status = _cairo_gl_context_acquire (device, &ctx); - if (unlikely (status)) - return status; - - _cairo_gl_composite_flush (ctx); - - _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); - _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); - - if (ctx->clip_region) { - cairo_region_destroy (ctx->clip_region); - ctx->clip_region = NULL; - } - - ctx->current_target = NULL; - ctx->current_operator = -1; - ctx->vertex_size = 0; - ctx->pre_shader = NULL; - _cairo_gl_set_shader (ctx, NULL); - - ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, 0); - - glDisable (GL_SCISSOR_TEST); - glDisable (GL_BLEND); - - return _cairo_gl_context_release (ctx, status); -} - -static void -_gl_finish (void *device) -{ - cairo_gl_context_t *ctx = device; - int n; - - _gl_lock (device); - - _cairo_cache_fini (&ctx->gradients); - - _cairo_gl_context_fini_shaders (ctx); - - for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) - _cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]); - - _gl_unlock (device); -} - -static void -_gl_destroy (void *device) -{ - cairo_gl_context_t *ctx = device; - - ctx->acquire (ctx); - - while (! cairo_list_is_empty (&ctx->fonts)) { - cairo_gl_font_t *font; - - font = cairo_list_first_entry (&ctx->fonts, - cairo_gl_font_t, - link); - - cairo_list_del (&font->base.link); - cairo_list_del (&font->link); - free (font); - } - - _cairo_array_fini (&ctx->tristrip_indices); - - cairo_region_destroy (ctx->clip_region); - _cairo_clip_destroy (ctx->clip); - - free (ctx->vb); - - ctx->destroy (ctx); - - free (ctx); -} - -static const cairo_device_backend_t _cairo_gl_device_backend = { - CAIRO_DEVICE_TYPE_GL, - - _gl_lock, - _gl_unlock, - - _gl_flush, /* flush */ - _gl_finish, - _gl_destroy, -}; - -static cairo_bool_t -_cairo_gl_msaa_compositor_enabled (void) -{ - const char *env = getenv ("CAIRO_GL_COMPOSITOR"); - return env && strcmp(env, "msaa") == 0; -} - -static cairo_bool_t -test_can_read_bgra (cairo_gl_flavor_t gl_flavor) -{ - /* Desktop GL always supports BGRA formats. */ - if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - return TRUE; - - assert (gl_flavor == CAIRO_GL_FLAVOR_ES3 || - gl_flavor == CAIRO_GL_FLAVOR_ES2); - - /* For OpenGL ES we have to look for the specific extension and BGRA only - * matches cairo's integer packed bytes on little-endian machines. */ - if (!_cairo_is_little_endian()) - return FALSE; - return _cairo_gl_has_extension ("EXT_read_format_bgra"); -} - -cairo_status_t -_cairo_gl_context_init (cairo_gl_context_t *ctx) -{ - cairo_status_t status; - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - int gl_version = _cairo_gl_get_version (); - cairo_gl_flavor_t gl_flavor = _cairo_gl_get_flavor (); - int n; - - cairo_bool_t is_desktop = gl_flavor == CAIRO_GL_FLAVOR_DESKTOP; - cairo_bool_t is_gles = (gl_flavor == CAIRO_GL_FLAVOR_ES3 || - gl_flavor == CAIRO_GL_FLAVOR_ES2); - - _cairo_device_init (&ctx->base, &_cairo_gl_device_backend); - - /* XXX The choice of compositor should be made automatically at runtime. - * However, it is useful to force one particular compositor whilst - * testing. - */ - if (_cairo_gl_msaa_compositor_enabled ()) - ctx->compositor = _cairo_gl_msaa_compositor_get (); - else - ctx->compositor = _cairo_gl_span_compositor_get (); - - - ctx->thread_aware = TRUE; - - memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache)); - cairo_list_init (&ctx->fonts); - - /* Support only GL version >= 1.3 */ - if (gl_version < CAIRO_GL_VERSION_ENCODE (1, 3)) - return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - - /* Check for required extensions */ - if (is_desktop) { - if (_cairo_gl_has_extension ("GL_ARB_texture_non_power_of_two")) { - ctx->tex_target = GL_TEXTURE_2D; - ctx->has_npot_repeat = TRUE; - } else if (_cairo_gl_has_extension ("GL_ARB_texture_rectangle")) { - ctx->tex_target = GL_TEXTURE_RECTANGLE; - ctx->has_npot_repeat = FALSE; - } else - return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - } else { - ctx->tex_target = GL_TEXTURE_2D; - if (_cairo_gl_has_extension ("GL_OES_texture_npot") || - _cairo_gl_has_extension ("GL_IMG_texture_npot")) - ctx->has_npot_repeat = TRUE; - else - ctx->has_npot_repeat = FALSE; - } - - if (is_desktop && gl_version < CAIRO_GL_VERSION_ENCODE (2, 1) && - ! _cairo_gl_has_extension ("GL_ARB_pixel_buffer_object")) - return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - - if (is_gles && ! _cairo_gl_has_extension ("GL_EXT_texture_format_BGRA8888")) - return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - - ctx->has_map_buffer = - is_desktop || (is_gles && _cairo_gl_has_extension ("GL_OES_mapbuffer")); - - ctx->can_read_bgra = test_can_read_bgra (gl_flavor); - - ctx->has_mesa_pack_invert = - _cairo_gl_has_extension ("GL_MESA_pack_invert"); - - ctx->has_packed_depth_stencil = - (is_desktop && _cairo_gl_has_extension ("GL_EXT_packed_depth_stencil")) || - (is_gles && _cairo_gl_has_extension ("GL_OES_packed_depth_stencil")); - - ctx->num_samples = 1; - -#if CAIRO_HAS_GL_SURFACE - if (is_desktop && ctx->has_packed_depth_stencil && - (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) || - _cairo_gl_has_extension ("GL_ARB_framebuffer_object") || - (_cairo_gl_has_extension ("GL_EXT_framebuffer_blit") && - _cairo_gl_has_extension ("GL_EXT_framebuffer_multisample")))) { - glGetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples); - } -#endif - -#if CAIRO_HAS_GLESV3_SURFACE - if (is_gles && ctx->has_packed_depth_stencil) { - glGetIntegerv(GL_MAX_SAMPLES, &ctx->num_samples); - } - -#elif CAIRO_HAS_GLESV2_SURFACE && defined(GL_MAX_SAMPLES_EXT) - if (is_gles && ctx->has_packed_depth_stencil && - _cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture")) { - glGetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples); - } - - if (is_gles && ctx->has_packed_depth_stencil && - _cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture")) { - glGetIntegerv(GL_MAX_SAMPLES_IMG, &ctx->num_samples); - } -#endif - - /* we always use renderbuffer for rendering in glesv3 */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - ctx->supports_msaa = TRUE; - else - ctx->supports_msaa = ctx->num_samples > 1; - if (ctx->num_samples > MAX_MSAA_SAMPLES) - ctx->num_samples = MAX_MSAA_SAMPLES; - - ctx->current_operator = -1; - ctx->gl_flavor = gl_flavor; - - status = _cairo_gl_context_init_shaders (ctx); - if (unlikely (status)) - return status; - - status = _cairo_cache_init (&ctx->gradients, - _cairo_gl_gradient_equal, - NULL, - (cairo_destroy_func_t) _cairo_gl_gradient_destroy, - CAIRO_GL_GRADIENT_CACHE_SIZE); - if (unlikely (status)) - return status; - - ctx->vbo_size = _cairo_gl_get_vbo_size(); - - ctx->vb = _cairo_malloc (ctx->vbo_size); - if (unlikely (ctx->vb == NULL)) { - _cairo_cache_fini (&ctx->gradients); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - ctx->primitive_type = CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES; - _cairo_array_init (&ctx->tristrip_indices, sizeof (unsigned short)); - - /* PBO for any sort of texture upload */ - dispatch->GenBuffers (1, &ctx->texture_load_pbo); - - ctx->max_framebuffer_size = 0; - glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size); - ctx->max_texture_size = 0; - glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size); - ctx->max_textures = 0; - glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures); - - for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) - _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]); - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gl_context_activate (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit) -{ - if (ctx->max_textures <= (GLint) tex_unit) { - if (tex_unit < 2) { - _cairo_gl_composite_flush (ctx); - _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1); - } - glActiveTexture (ctx->max_textures - 1); - } else { - glActiveTexture (GL_TEXTURE0 + tex_unit); - } -} - -static GLenum -_get_depth_stencil_format (cairo_gl_context_t *ctx) -{ - /* This is necessary to properly handle the situation where both - OpenGL and OpenGLES are active and returning a sane default. */ -#if CAIRO_HAS_GL_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - return GL_DEPTH_STENCIL; -#endif - -#if CAIRO_HAS_GLESV2_SURFACE && !CAIRO_HAS_GLESV3_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - return GL_DEPTH24_STENCIL8_OES; -#endif - -#if CAIRO_HAS_GL_SURFACE - return GL_DEPTH_STENCIL; -#elif CAIRO_HAS_GLESV3_SURFACE - return GL_DEPTH24_STENCIL8; -#elif CAIRO_HAS_GLESV2_SURFACE - return GL_DEPTH24_STENCIL8_OES; -#endif -} - -#if CAIRO_HAS_GLESV2_SURFACE -static void -_cairo_gl_ensure_msaa_gles_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - if (surface->msaa_active) - return; - - ctx->dispatch.FramebufferTexture2DMultisample(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - ctx->tex_target, - surface->tex, - 0, - ctx->num_samples); - - /* From now on MSAA will always be active on this surface. */ - surface->msaa_active = TRUE; -} -#endif - -void -_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - GLenum status; - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - - if (likely (surface->fb)) - return; - - /* Create a framebuffer object wrapping the texture so that we can render - * to it. - */ - dispatch->GenFramebuffers (1, &surface->fb); - dispatch->BindFramebuffer (GL_FRAMEBUFFER, surface->fb); - - /* Unlike for desktop GL we only maintain one multisampling framebuffer - for OpenGLES since the EXT_multisampled_render_to_texture extension - does not require an explicit multisample resolution. */ -#if CAIRO_HAS_GLESV2_SURFACE - if (surface->supports_msaa && _cairo_gl_msaa_compositor_enabled () && - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { - _cairo_gl_ensure_msaa_gles_framebuffer (ctx, surface); - } else -#endif - dispatch->FramebufferTexture2D (GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - ctx->tex_target, - surface->tex, - 0); - -#if CAIRO_HAS_GL_SURFACE - glDrawBuffer (GL_COLOR_ATTACHMENT0); - glReadBuffer (GL_COLOR_ATTACHMENT0); -#endif - - status = dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - const char *str; - switch (status) { - //case GL_FRAMEBUFFER_UNDEFINED: str= "undefined"; break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: str= "incomplete attachment"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: str= "incomplete/missing attachment"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: str= "incomplete draw buffer"; break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: str= "incomplete read buffer"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: str= "unsupported"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: str= "incomplete multiple"; break; - default: str = "unknown error"; break; - } - - fprintf (stderr, - "destination is framebuffer incomplete: %s [%#x]\n", - str, status); - } -} -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE -static void -_cairo_gl_ensure_multisampling (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - assert (surface->supports_msaa); - assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3); - - if (surface->msaa_fb) - return; - - /* We maintain a separate framebuffer for multisampling operations. - This allows us to do a fast paint to the non-multisampling framebuffer - when mulitsampling is disabled. */ - ctx->dispatch.GenFramebuffers (1, &surface->msaa_fb); - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb); - ctx->dispatch.GenRenderbuffers (1, &surface->msaa_rb); - ctx->dispatch.BindRenderbuffer (GL_RENDERBUFFER, surface->msaa_rb); - - /* FIXME: For now we assume that textures passed from the outside have GL_RGBA - format, but eventually we need to expose a way for the API consumer to pass - this information. */ - ctx->dispatch.RenderbufferStorageMultisample (GL_RENDERBUFFER, - ctx->num_samples, -#if CAIRO_HAS_GLESV3_SURFACE - GL_RGBA8, -#else - GL_RGBA, -#endif - surface->width, - surface->height); - ctx->dispatch.FramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, - surface->msaa_rb); - - /* Cairo surfaces start out initialized to transparent (black) */ - glDisable (GL_SCISSOR_TEST); - glClearColor (0, 0, 0, 0); - glClear (GL_COLOR_BUFFER_BIT); - - /* for glesv3 with multisample renderbuffer, we always render to - this renderbuffer */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - surface->msaa_active = TRUE; -} -#endif - -static cairo_bool_t -_cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - if (surface->msaa_depth_stencil) - return TRUE; - - _cairo_gl_ensure_framebuffer (ctx, surface); -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - _cairo_gl_ensure_multisampling (ctx, surface); -#endif - - dispatch->GenRenderbuffers (1, &surface->msaa_depth_stencil); - dispatch->BindRenderbuffer (GL_RENDERBUFFER, - surface->msaa_depth_stencil); - - dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER, - ctx->num_samples, - _get_depth_stencil_format (ctx), - surface->width, - surface->height); - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) { - dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_DEPTH_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, - surface->msaa_depth_stencil); - } -#endif - -#if CAIRO_HAS_GLESV2_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { - dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, - surface->msaa_depth_stencil); - dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, - surface->msaa_depth_stencil); - } -#endif - - if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - dispatch->DeleteRenderbuffers (1, &surface->msaa_depth_stencil); - surface->msaa_depth_stencil = 0; - return FALSE; - } - - return TRUE; -} - -static cairo_bool_t -_cairo_gl_ensure_depth_stencil_buffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - - if (surface->depth_stencil) - return TRUE; - - _cairo_gl_ensure_framebuffer (ctx, surface); - - dispatch->GenRenderbuffers (1, &surface->depth_stencil); - dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->depth_stencil); - dispatch->RenderbufferStorage (GL_RENDERBUFFER, - _get_depth_stencil_format (ctx), - surface->width, surface->height); - - dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, surface->depth_stencil); - dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, surface->depth_stencil); - if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - dispatch->DeleteRenderbuffers (1, &surface->depth_stencil); - surface->depth_stencil = 0; - return FALSE; - } - - return TRUE; -} - -cairo_bool_t -_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - if (! _cairo_gl_surface_is_texture (surface)) - return TRUE; /* best guess for now, will check later */ - if (! ctx->has_packed_depth_stencil) - return FALSE; - - if (surface->msaa_active) - return _cairo_gl_ensure_msaa_depth_stencil_buffer (ctx, surface); - else - return _cairo_gl_ensure_depth_stencil_buffer (ctx, surface); -} - -/* - * Stores a parallel projection transformation in matrix 'm', - * using column-major order. - * - * This is equivalent to: - * - * glLoadIdentity() - * gluOrtho2D() - * - * The calculation for the ortho transformation was taken from the - * mesa source code. - */ -static void -_gl_identity_ortho (GLfloat *m, - GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top) -{ -#define M(row,col) m[col*4+row] - M(0,0) = 2.f / (right - left); - M(0,1) = 0.f; - M(0,2) = 0.f; - M(0,3) = -(right + left) / (right - left); - - M(1,0) = 0.f; - M(1,1) = 2.f / (top - bottom); - M(1,2) = 0.f; - M(1,3) = -(top + bottom) / (top - bottom); - - M(2,0) = 0.f; - M(2,1) = 0.f; - M(2,2) = -1.f; - M(2,3) = 0.f; - - M(3,0) = 0.f; - M(3,1) = 0.f; - M(3,2) = 0.f; - M(3,3) = 1.f; -#undef M -} - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE -static void -bind_multisample_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - cairo_bool_t stencil_test_enabled; - cairo_bool_t scissor_test_enabled; - - assert (surface->supports_msaa); - assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3); - - _cairo_gl_ensure_framebuffer (ctx, surface); - _cairo_gl_ensure_multisampling (ctx, surface); - - if (surface->msaa_active) { -#if CAIRO_HAS_GL_SURFACE - glEnable (GL_MULTISAMPLE); -#endif - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - surface->content_in_texture = FALSE; - return; - } - - _cairo_gl_composite_flush (ctx); - - stencil_test_enabled = glIsEnabled (GL_STENCIL_TEST); - scissor_test_enabled = glIsEnabled (GL_SCISSOR_TEST); - glDisable (GL_STENCIL_TEST); - glDisable (GL_SCISSOR_TEST); - -#if CAIRO_HAS_GL_SURFACE - glEnable (GL_MULTISAMPLE); -#endif - - /* The last time we drew to the surface, we were not using multisampling, - so we need to blit from the non-multisampling framebuffer into the - multisampling framebuffer. */ - ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->msaa_fb); - ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->fb); - ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height, - 0, 0, surface->width, surface->height, - GL_COLOR_BUFFER_BIT -#if CAIRO_HAS_GL_SURFACE - | GL_STENCIL_BUFFER_BIT -#endif - , - GL_NEAREST); - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb); - - if (stencil_test_enabled) - glEnable (GL_STENCIL_TEST); - if (scissor_test_enabled) - glEnable (GL_SCISSOR_TEST); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - surface->content_in_texture = FALSE; -} -#endif - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE -static void -bind_singlesample_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface) -{ - cairo_bool_t stencil_test_enabled; - cairo_bool_t scissor_test_enabled; - - assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3); - _cairo_gl_ensure_framebuffer (ctx, surface); - - if (! surface->msaa_active) { -#if CAIRO_HAS_GL_SURFACE - glDisable (GL_MULTISAMPLE); -#endif - - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); - return; - } - - _cairo_gl_composite_flush (ctx); - - stencil_test_enabled = glIsEnabled (GL_STENCIL_TEST); - scissor_test_enabled = glIsEnabled (GL_SCISSOR_TEST); - glDisable (GL_STENCIL_TEST); - glDisable (GL_SCISSOR_TEST); - -#if CAIRO_HAS_GL_SURFACE - glDisable (GL_MULTISAMPLE); -#endif - - /* The last time we drew to the surface, we were using multisampling, - so we need to blit from the multisampling framebuffer into the - non-multisampling framebuffer. */ - ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb); - ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb); - ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height, - 0, 0, surface->width, surface->height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); - - if (stencil_test_enabled) - glEnable (GL_STENCIL_TEST); - if (scissor_test_enabled) - glEnable (GL_SCISSOR_TEST); -} -#endif - -void -_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface, - cairo_bool_t multisampling) -{ - if (_cairo_gl_surface_is_texture (surface)) { - /* OpenGL ES surfaces only have either a multisample framebuffer or a - * singlesample framebuffer, so we cannot switch back and forth. */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { - _cairo_gl_ensure_framebuffer (ctx, surface); - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); - return; - } - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE - if (multisampling) - bind_multisample_framebuffer (ctx, surface); - else - bind_singlesample_framebuffer (ctx, surface); -#endif - } else { - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0); - -#if CAIRO_HAS_GL_SURFACE - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { - if (multisampling) - glEnable (GL_MULTISAMPLE); - else - glDisable (GL_MULTISAMPLE); - } -#endif - } - - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - surface->msaa_active = multisampling; -} - -void -_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface, - cairo_bool_t multisampling) -{ - cairo_bool_t changing_surface, changing_sampling; - - /* The decision whether or not to use multisampling happens when - * we create an OpenGL ES surface, so we can never switch modes. */ - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) - multisampling = surface->msaa_active; - /* For GLESV3, we always use renderbuffer for drawing */ - else if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - multisampling = TRUE; - - changing_surface = ctx->current_target != surface || surface->needs_update; - changing_sampling = (surface->msaa_active != multisampling || - surface->content_in_texture); - if (! changing_surface && ! changing_sampling) - return; - - if (! changing_surface) { - _cairo_gl_composite_flush (ctx); - _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling); - return; - } - - _cairo_gl_composite_flush (ctx); - - ctx->current_target = surface; - surface->needs_update = FALSE; - - if (! _cairo_gl_surface_is_texture (surface)) { - ctx->make_current (ctx, surface); - } - - _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling); - - if (! _cairo_gl_surface_is_texture (surface)) { -#if CAIRO_HAS_GL_SURFACE - glDrawBuffer (GL_BACK_LEFT); - glReadBuffer (GL_BACK_LEFT); -#endif - } - - glDisable (GL_DITHER); - glViewport (0, 0, surface->width, surface->height); - - if (_cairo_gl_surface_is_texture (surface)) - _gl_identity_ortho (ctx->modelviewprojection_matrix, - 0, surface->width, 0, surface->height); - else - _gl_identity_ortho (ctx->modelviewprojection_matrix, - 0, surface->width, surface->height, 0); -} - -void -cairo_gl_device_set_thread_aware (cairo_device_t *device, - cairo_bool_t thread_aware) -{ - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return; - } - ((cairo_gl_context_t *) device)->thread_aware = thread_aware; -} diff --git a/src/cairo-gl-dispatch-private.h b/src/cairo-gl-dispatch-private.h deleted file mode 100644 index cabf76f0d..000000000 --- a/src/cairo-gl-dispatch-private.h +++ /dev/null @@ -1,129 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2010 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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. - * - * Contributor(s): - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - */ - -#ifndef CAIRO_GL_DISPATCH_PRIVATE_H -#define CAIRO_GL_DISPATCH_PRIVATE_H - -#include "cairo-gl-private.h" -#include <stddef.h> - -typedef enum _cairo_gl_dispatch_name { - CAIRO_GL_DISPATCH_NAME_CORE, - CAIRO_GL_DISPATCH_NAME_EXT, - CAIRO_GL_DISPATCH_NAME_ES, - CAIRO_GL_DISPATCH_NAME_COUNT -} cairo_gl_dispatch_name_t; - -typedef struct _cairo_gl_dispatch_entry { - const char *name[CAIRO_GL_DISPATCH_NAME_COUNT]; - size_t offset; -} cairo_gl_dispatch_entry_t; - -#define DISPATCH_ENTRY_ARB(name) { { "gl"#name, "gl"#name"ARB", "gl"#name }, \ - offsetof(cairo_gl_dispatch_t, name) } -#define DISPATCH_ENTRY_EXT(name) { { "gl"#name, "gl"#name"EXT", "gl"#name }, \ - offsetof(cairo_gl_dispatch_t, name) } -#define DISPATCH_ENTRY_ARB_OES(name) { { "gl"#name, "gl"#name"ARB", "gl"#name"OES" }, \ - offsetof(cairo_gl_dispatch_t, name) } -#define DISPATCH_ENTRY_EXT_IMG(name) { { "gl"#name, "gl"#name"EXT", "gl"#name"IMG" }, \ - offsetof(cairo_gl_dispatch_t, name) } -#define DISPATCH_ENTRY_CUSTOM(name, name2) { { "gl"#name, "gl"#name2, "gl"#name }, \ - offsetof(cairo_gl_dispatch_t, name)} -#define DISPATCH_ENTRY_LAST { { NULL, NULL, NULL }, 0 } - -cairo_private cairo_gl_dispatch_entry_t dispatch_buffers_entries[] = { - DISPATCH_ENTRY_ARB (GenBuffers), - DISPATCH_ENTRY_ARB (BindBuffer), - DISPATCH_ENTRY_ARB (BufferData), - DISPATCH_ENTRY_ARB_OES (MapBuffer), - DISPATCH_ENTRY_ARB_OES (UnmapBuffer), - DISPATCH_ENTRY_LAST -}; - -cairo_private cairo_gl_dispatch_entry_t dispatch_shaders_entries[] = { - /* Shaders */ - DISPATCH_ENTRY_CUSTOM (CreateShader, CreateShaderObjectARB), - DISPATCH_ENTRY_ARB (ShaderSource), - DISPATCH_ENTRY_ARB (CompileShader), - DISPATCH_ENTRY_CUSTOM (GetShaderiv, GetObjectParameterivARB), - DISPATCH_ENTRY_CUSTOM (GetShaderInfoLog, GetInfoLogARB), - DISPATCH_ENTRY_CUSTOM (DeleteShader, DeleteObjectARB), - - /* Programs */ - DISPATCH_ENTRY_CUSTOM (CreateProgram, CreateProgramObjectARB), - DISPATCH_ENTRY_CUSTOM (AttachShader, AttachObjectARB), - DISPATCH_ENTRY_CUSTOM (DeleteProgram, DeleteObjectARB), - DISPATCH_ENTRY_ARB (LinkProgram), - DISPATCH_ENTRY_CUSTOM (UseProgram, UseProgramObjectARB), - DISPATCH_ENTRY_CUSTOM (GetProgramiv, GetObjectParameterivARB), - DISPATCH_ENTRY_CUSTOM (GetProgramInfoLog, GetInfoLogARB), - - /* Uniforms */ - DISPATCH_ENTRY_ARB (GetUniformLocation), - DISPATCH_ENTRY_ARB (Uniform1f), - DISPATCH_ENTRY_ARB (Uniform2f), - DISPATCH_ENTRY_ARB (Uniform3f), - DISPATCH_ENTRY_ARB (Uniform4f), - DISPATCH_ENTRY_ARB (UniformMatrix3fv), - DISPATCH_ENTRY_ARB (UniformMatrix4fv), - DISPATCH_ENTRY_ARB (Uniform1i), - - /* Attributes */ - DISPATCH_ENTRY_ARB (BindAttribLocation), - DISPATCH_ENTRY_ARB (VertexAttribPointer), - DISPATCH_ENTRY_ARB (EnableVertexAttribArray), - DISPATCH_ENTRY_ARB (DisableVertexAttribArray), - - DISPATCH_ENTRY_LAST -}; - -cairo_private cairo_gl_dispatch_entry_t dispatch_fbo_entries[] = { - DISPATCH_ENTRY_EXT (GenFramebuffers), - DISPATCH_ENTRY_EXT (BindFramebuffer), - DISPATCH_ENTRY_EXT (FramebufferTexture2D), - DISPATCH_ENTRY_EXT (CheckFramebufferStatus), - DISPATCH_ENTRY_EXT (DeleteFramebuffers), - DISPATCH_ENTRY_EXT (GenRenderbuffers), - DISPATCH_ENTRY_EXT (BindRenderbuffer), - DISPATCH_ENTRY_EXT (RenderbufferStorage), - DISPATCH_ENTRY_EXT (FramebufferRenderbuffer), - DISPATCH_ENTRY_EXT (DeleteRenderbuffers), - DISPATCH_ENTRY_EXT (BlitFramebuffer), - DISPATCH_ENTRY_LAST -}; - -cairo_private cairo_gl_dispatch_entry_t dispatch_multisampling_entries[] = { - DISPATCH_ENTRY_EXT_IMG (RenderbufferStorageMultisample), - DISPATCH_ENTRY_EXT_IMG (FramebufferTexture2DMultisample), - DISPATCH_ENTRY_LAST -}; - -#endif /* CAIRO_GL_DISPATCH_PRIVATE_H */ diff --git a/src/cairo-gl-dispatch.c b/src/cairo-gl-dispatch.c deleted file mode 100644 index a49199dbb..000000000 --- a/src/cairo-gl-dispatch.c +++ /dev/null @@ -1,273 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2010 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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. - * - * Contributor(s): - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - */ - -#include "cairoint.h" -#include "cairo-gl-private.h" -#include "cairo-gl-dispatch-private.h" -#if CAIRO_HAS_DLSYM -#include <dlfcn.h> -#endif - -#if CAIRO_HAS_DLSYM -static void * -_cairo_gl_dispatch_open_lib (void) -{ - return dlopen (NULL, RTLD_LAZY); -} - -static void -_cairo_gl_dispatch_close_lib (void *handle) -{ - dlclose (handle); -} - -static cairo_gl_generic_func_t -_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name) -{ - return (cairo_gl_generic_func_t) dlsym (handle, name); -} -#else -static void * -_cairo_gl_dispatch_open_lib (void) -{ - return NULL; -} - -static void -_cairo_gl_dispatch_close_lib (void *handle) -{ - return; -} - -static cairo_gl_generic_func_t -_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name) -{ - return NULL; -} -#endif /* CAIRO_HAS_DLSYM */ - - -static void -_cairo_gl_dispatch_init_entries (cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr, - cairo_gl_dispatch_entry_t *entries, - cairo_gl_dispatch_name_t dispatch_name) -{ - cairo_gl_dispatch_entry_t *entry = entries; - void *handle = _cairo_gl_dispatch_open_lib (); - - while (entry->name[CAIRO_GL_DISPATCH_NAME_CORE] != NULL) { - void *dispatch_ptr = &((char *) dispatch)[entry->offset]; - const char *name = entry->name[dispatch_name]; - - /* - * In strictly conforming EGL implementations, eglGetProcAddress() can - * be used only to get extension functions, but some of the functions - * we want belong to core GL(ES). If the *GetProcAddress function - * provided by the context fails, try to get the address of the wanted - * GL function using standard system facilities (eg dlsym() in *nix - * systems). - */ - cairo_gl_generic_func_t func = get_proc_addr (name); - if (func == NULL) - func = _cairo_gl_dispatch_get_proc_addr (handle, name); - - *((cairo_gl_generic_func_t *) dispatch_ptr) = func; - - ++entry; - } - - _cairo_gl_dispatch_close_lib (handle); -} - -static cairo_status_t -_cairo_gl_dispatch_init_buffers (cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr, - int gl_version, cairo_gl_flavor_t gl_flavor) -{ - cairo_gl_dispatch_name_t dispatch_name; - - if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - { - if (gl_version >= CAIRO_GL_VERSION_ENCODE (1, 5)) - dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - else if (_cairo_gl_has_extension ("GL_ARB_vertex_buffer_object")) - dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; - else - return CAIRO_STATUS_DEVICE_ERROR; - } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES3) - { - dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 && - gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) - { - dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; - } - else - { - return CAIRO_STATUS_DEVICE_ERROR; - } - - _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, - dispatch_buffers_entries, dispatch_name); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_dispatch_init_shaders (cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr, - int gl_version, cairo_gl_flavor_t gl_flavor) -{ - cairo_gl_dispatch_name_t dispatch_name; - - if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - { - if (gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) - dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - else if (_cairo_gl_has_extension ("GL_ARB_shader_objects")) - dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; - else - return CAIRO_STATUS_DEVICE_ERROR; - } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES3) - { - dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 && - gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) - { - dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; - } - else - { - return CAIRO_STATUS_DEVICE_ERROR; - } - - _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, - dispatch_shaders_entries, dispatch_name); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_dispatch_init_fbo (cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr, - int gl_version, cairo_gl_flavor_t gl_flavor) -{ - cairo_gl_dispatch_name_t dispatch_name; - - if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) - { - if (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) || - _cairo_gl_has_extension ("GL_ARB_framebuffer_object")) - dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - else if (_cairo_gl_has_extension ("GL_EXT_framebuffer_object")) - dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; - else - return CAIRO_STATUS_DEVICE_ERROR; - } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES3) - { - dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - } - else if (gl_flavor == CAIRO_GL_FLAVOR_ES2 && - gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) - { - dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; - } - else - { - return CAIRO_STATUS_DEVICE_ERROR; - } - - _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, - dispatch_fbo_entries, dispatch_name); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_dispatch_init_multisampling (cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr, - int gl_version, - cairo_gl_flavor_t gl_flavor) -{ - /* For the multisampling table, there are two GLES versions of the - * extension, so we put one in the EXT slot and one in the real ES slot.*/ - cairo_gl_dispatch_name_t dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; - if (gl_flavor == CAIRO_GL_FLAVOR_ES2) { - if (_cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture")) - dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; - else if (_cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture")) - dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; - } - _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, - dispatch_multisampling_entries, - dispatch_name); - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_gl_dispatch_init (cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr) -{ - cairo_status_t status; - int gl_version; - cairo_gl_flavor_t gl_flavor; - - gl_version = _cairo_gl_get_version (); - gl_flavor = _cairo_gl_get_flavor (); - - status = _cairo_gl_dispatch_init_buffers (dispatch, get_proc_addr, - gl_version, gl_flavor); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - status = _cairo_gl_dispatch_init_shaders (dispatch, get_proc_addr, - gl_version, gl_flavor); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - status = _cairo_gl_dispatch_init_fbo (dispatch, get_proc_addr, - gl_version, gl_flavor); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - status = _cairo_gl_dispatch_init_multisampling (dispatch, get_proc_addr, - gl_version, gl_flavor); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo-gl-ext-def-private.h b/src/cairo-gl-ext-def-private.h deleted file mode 100644 index a261947be..000000000 --- a/src/cairo-gl-ext-def-private.h +++ /dev/null @@ -1,143 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2010 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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. - * - * Contributor(s): - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - */ - -#ifndef CAIRO_GL_EXT_DEF_PRIVATE_H -#define CAIRO_GL_EXT_DEF_PRIVATE_H - -#ifndef GL_TEXTURE_RECTANGLE -#define GL_TEXTURE_RECTANGLE 0x84F5 -#endif - -#ifndef GL_ARRAY_BUFFER -#define GL_ARRAY_BUFFER 0x8892 -#endif - -#ifndef GL_STREAM_DRAW -#define GL_STREAM_DRAW 0x88E0 -#endif - -#ifndef GL_WRITE_ONLY -#define GL_WRITE_ONLY 0x88B9 -#endif - -#ifndef GL_PIXEL_UNPACK_BUFFER -#define GL_PIXEL_UNPACK_BUFFER 0x88EC -#endif - -#ifndef GL_FRAMEBUFFER -#define GL_FRAMEBUFFER 0x8D40 -#endif - -#ifndef GL_COLOR_ATTACHMENT0 -#define GL_COLOR_ATTACHMENT0 0x8CE0 -#endif - -#ifndef GL_FRAMEBUFFER_COMPLETE -#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 0x8CDA -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC -#endif - -#ifndef GL_FRAMEBUFFER_UNSUPPORTED -#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD -#endif - -#ifndef GL_PACK_INVERT_MESA -#define GL_PACK_INVERT_MESA 0x8758 -#endif - -#ifndef GL_CLAMP_TO_BORDER -#define GL_CLAMP_TO_BORDER 0x812D -#endif - -#ifndef GL_BGR -#define GL_BGR 0x80E0 -#endif - -#ifndef GL_BGRA -#define GL_BGRA 0x80E1 -#endif - -#ifndef GL_RGBA8 -#define GL_RGBA8 0x8058 -#endif - -#ifndef GL_UNSIGNED_INT_8_8_8_8 -#define GL_UNSIGNED_INT_8_8_8_8 0x8035 -#endif - -#ifndef GL_UNSIGNED_SHORT_5_6_5_REV -#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 -#endif - -#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV -#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 -#endif - -#ifndef GL_UNSIGNED_INT_8_8_8_8_REV -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 -#endif - -#ifndef GL_PACK_ROW_LENGTH -#define GL_PACK_ROW_LENGTH 0x0D02 -#endif - -#ifndef GL_UNPACK_ROW_LENGTH -#define GL_UNPACK_ROW_LENGTH 0x0CF2 -#endif - -#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE -#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 -#endif - -#endif /* CAIRO_GL_EXT_DEF_PRIVATE_H */ diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c deleted file mode 100644 index f6f5ec096..000000000 --- a/src/cairo-gl-glyphs.c +++ /dev/null @@ -1,507 +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 © 2009 Chris Wilson - * Copyright © 2010 Intel Corporation - * Copyright © 2010 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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. - * - * Contributors: - * Benjamin Otte <otte@gnome.org> - * Chris Wilson <chris@chris-wilson.co.uk> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-compositor-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-image-surface-private.h" -#include "cairo-rtree-private.h" - -#define GLYPH_CACHE_WIDTH 1024 -#define GLYPH_CACHE_HEIGHT 1024 -#define GLYPH_CACHE_MIN_SIZE 4 -#define GLYPH_CACHE_MAX_SIZE 128 - -typedef struct _cairo_gl_glyph { - cairo_rtree_node_t node; - cairo_scaled_glyph_private_t base; - cairo_scaled_glyph_t *glyph; - cairo_gl_glyph_cache_t *cache; - struct { float x, y; } p1, p2; -} cairo_gl_glyph_t; - -static void -_cairo_gl_node_destroy (cairo_rtree_node_t *node) -{ - cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node); - cairo_scaled_glyph_t *glyph; - - glyph = priv->glyph; - if (glyph == NULL) - return; - - if (glyph->dev_private_key == priv->cache) { - glyph->dev_private = NULL; - glyph->dev_private_key = NULL; - } - cairo_list_del (&priv->base.link); - priv->glyph = NULL; -} - -static void -_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, - cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_gl_glyph_t *priv = cairo_container_of (glyph_private, - cairo_gl_glyph_t, - base); - - assert (priv->glyph); - - _cairo_gl_node_destroy (&priv->node); - - /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ - if (! priv->node.pinned) - _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node); - - assert (priv->glyph == NULL); -} - -static cairo_int_status_t -_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, - cairo_gl_glyph_cache_t *cache, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_gl_glyph_t *glyph_private; - cairo_rtree_node_t *node = NULL; - cairo_int_status_t status; - int width, height; - - width = glyph_surface->width; - if (width < GLYPH_CACHE_MIN_SIZE) - width = GLYPH_CACHE_MIN_SIZE; - height = glyph_surface->height; - if (height < GLYPH_CACHE_MIN_SIZE) - height = GLYPH_CACHE_MIN_SIZE; - - /* search for an available slot */ - status = _cairo_rtree_insert (&cache->rtree, width, height, &node); - /* search for an unlocked slot */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_rtree_evict_random (&cache->rtree, - width, height, &node); - if (status == CAIRO_INT_STATUS_SUCCESS) { - status = _cairo_rtree_node_insert (&cache->rtree, - node, width, height, &node); - } - } - if (status) - return status; - - /* XXX: Make sure we use the mask texture. This should work automagically somehow */ - glActiveTexture (GL_TEXTURE1); - status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface, - 0, 0, - glyph_surface->width, glyph_surface->height, - node->x, node->y, FALSE); - if (unlikely (status)) - return status; - - glyph_private = (cairo_gl_glyph_t *) node; - glyph_private->cache = cache; - glyph_private->glyph = scaled_glyph; - _cairo_scaled_glyph_attach_private (scaled_glyph, - &glyph_private->base, - cache, - _cairo_gl_glyph_fini); - - scaled_glyph->dev_private = glyph_private; - scaled_glyph->dev_private_key = cache; - - /* compute tex coords */ - glyph_private->p1.x = node->x; - glyph_private->p1.y = node->y; - glyph_private->p2.x = node->x + glyph_surface->width; - glyph_private->p2.y = node->y + glyph_surface->height; - if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) { - glyph_private->p1.x /= GLYPH_CACHE_WIDTH; - glyph_private->p2.x /= GLYPH_CACHE_WIDTH; - glyph_private->p1.y /= GLYPH_CACHE_HEIGHT; - glyph_private->p2.y /= GLYPH_CACHE_HEIGHT; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_gl_glyph_t * -_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, - cairo_scaled_glyph_t *scaled_glyph) -{ - return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private); -} - -static cairo_status_t -cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, - cairo_format_t format, - cairo_gl_glyph_cache_t **cache_out) -{ - cairo_gl_glyph_cache_t *cache; - cairo_content_t content; - - switch (format) { - case CAIRO_FORMAT_RGBA128F: - case CAIRO_FORMAT_RGB96F: - case CAIRO_FORMAT_RGB30: - case CAIRO_FORMAT_RGB16_565: - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - cache = &ctx->glyph_cache[0]; - content = CAIRO_CONTENT_COLOR_ALPHA; - break; - case CAIRO_FORMAT_A8: - case CAIRO_FORMAT_A1: - cache = &ctx->glyph_cache[1]; - content = CAIRO_CONTENT_ALPHA; - break; - default: - case CAIRO_FORMAT_INVALID: - ASSERT_NOT_REACHED; - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - } - - if (unlikely (cache->surface == NULL)) { - cairo_surface_t *surface; - - surface = _cairo_gl_surface_create_scratch_for_caching (ctx, - content, - GLYPH_CACHE_WIDTH, - GLYPH_CACHE_HEIGHT); - if (unlikely (surface->status)) - return surface->status; - - _cairo_surface_release_device_reference (surface); - - cache->surface = (cairo_gl_surface_t *)surface; - cache->surface->operand.texture.attributes.has_component_alpha = - content == CAIRO_CONTENT_COLOR_ALPHA; - } - - *cache_out = cache; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -render_glyphs (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - cairo_operator_t op, - cairo_surface_t *source, - cairo_composite_glyphs_info_t *info, - cairo_bool_t *has_component_alpha, - cairo_clip_t *clip) -{ - cairo_format_t last_format = CAIRO_FORMAT_INVALID; - cairo_gl_glyph_cache_t *cache = NULL; - cairo_gl_context_t *ctx; - cairo_gl_emit_glyph_t emit = NULL; - cairo_gl_composite_t setup; - cairo_int_status_t status; - int i = 0; - - TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__, - info->extents.x, info->extents.y, - info->extents.width, info->extents.height)); - - *has_component_alpha = FALSE; - - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - status = _cairo_gl_composite_init (&setup, op, dst, TRUE); - if (unlikely (status)) - goto FINISH; - - if (source == NULL) { - _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE); - } else { - _cairo_gl_composite_set_source_operand (&setup, - source_to_operand (source)); - - } - - _cairo_gl_composite_set_clip (&setup, clip); - - for (i = 0; i < info->num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - cairo_gl_glyph_t *glyph; - double x_offset, y_offset; - double x1, x2, y1, y2; - - status = _cairo_scaled_glyph_lookup (info->font, - info->glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - NULL, /* foreground color */ - &scaled_glyph); - if (unlikely (status)) - goto FINISH; - - if (scaled_glyph->surface->width == 0 || - scaled_glyph->surface->height == 0) - { - continue; - } - if (scaled_glyph->surface->format != last_format) { - status = cairo_gl_context_get_glyph_cache (ctx, - scaled_glyph->surface->format, - &cache); - if (unlikely (status)) - goto FINISH; - - last_format = scaled_glyph->surface->format; - - _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); - *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; - - /* XXX Shoot me. */ - status = _cairo_gl_composite_begin (&setup, &ctx); - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) - goto FINISH; - - emit = _cairo_gl_context_choose_emit_glyph (ctx); - } - - if (scaled_glyph->dev_private_key != cache) { - cairo_scaled_glyph_private_t *priv; - - priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache); - if (priv) { - scaled_glyph->dev_private_key = cache; - scaled_glyph->dev_private = cairo_container_of (priv, - cairo_gl_glyph_t, - base); - } else { - status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - /* Cache is full, so flush existing prims and try again. */ - _cairo_gl_composite_flush (ctx); - _cairo_gl_glyph_cache_unlock (cache); - status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); - } - - if (unlikely (_cairo_int_status_is_error (status))) - goto FINISH; - } - } - - x_offset = scaled_glyph->surface->base.device_transform.x0; - y_offset = scaled_glyph->surface->base.device_transform.y0; - - x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x); - y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y); - x2 = x1 + scaled_glyph->surface->width; - y2 = y1 + scaled_glyph->surface->height; - - glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); - assert (emit); - emit (ctx, - x1, y1, x2, y2, - glyph->p1.x, glyph->p1.y, - glyph->p2.x, glyph->p2.y); - } - - status = CAIRO_STATUS_SUCCESS; - FINISH: - status = _cairo_gl_context_release (ctx, status); - - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_int_status_t -render_glyphs_via_mask (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - cairo_operator_t op, - cairo_surface_t *source, - cairo_composite_glyphs_info_t *info, - cairo_clip_t *clip) -{ - cairo_surface_t *mask; - cairo_status_t status; - cairo_bool_t has_component_alpha; - - TRACE ((stderr, "%s\n", __FUNCTION__)); - - /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ - mask = cairo_gl_surface_create (dst->base.device, - CAIRO_CONTENT_COLOR_ALPHA, - info->extents.width, - info->extents.height); - if (unlikely (mask->status)) - return mask->status; - - status = render_glyphs ((cairo_gl_surface_t *) mask, - info->extents.x, info->extents.y, - CAIRO_OPERATOR_ADD, NULL, - info, &has_component_alpha, NULL); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - cairo_surface_pattern_t mask_pattern; - cairo_surface_pattern_t source_pattern; - cairo_rectangle_int_t clip_extents; - - mask->is_clear = FALSE; - _cairo_pattern_init_for_surface (&mask_pattern, mask); - mask_pattern.base.has_component_alpha = has_component_alpha; - mask_pattern.base.filter = CAIRO_FILTER_NEAREST; - mask_pattern.base.extend = CAIRO_EXTEND_NONE; - - cairo_matrix_init_translate (&mask_pattern.base.matrix, - dst_x-info->extents.x, dst_y-info->extents.y); - - _cairo_pattern_init_for_surface (&source_pattern, source); - cairo_matrix_init_translate (&source_pattern.base.matrix, - dst_x-info->extents.x, dst_y-info->extents.y); - - clip = _cairo_clip_copy (clip); - clip_extents.x = info->extents.x - dst_x; - clip_extents.y = info->extents.y - dst_y; - clip_extents.width = info->extents.width; - clip_extents.height = info->extents.height; - clip = _cairo_clip_intersect_rectangle (clip, &clip_extents); - - status = _cairo_surface_mask (&dst->base, op, - &source_pattern.base, - &mask_pattern.base, - clip); - - _cairo_clip_destroy (clip); - - _cairo_pattern_fini (&mask_pattern.base); - _cairo_pattern_fini (&source_pattern.base); - } - - cairo_surface_destroy (mask); - - return status; -} - -cairo_int_status_t -_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int *num_glyphs) -{ - if (! _cairo_gl_operator_is_supported (extents->op)) - return UNSUPPORTED ("unsupported operator"); - - /* XXX use individual masks for large glyphs? */ - if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE) - return UNSUPPORTED ("glyphs too large"); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_gl_composite_glyphs_with_clip (void *_dst, - cairo_operator_t op, - cairo_surface_t *_src, - int src_x, - int src_y, - int dst_x, - int dst_y, - cairo_composite_glyphs_info_t *info, - cairo_clip_t *clip) -{ - cairo_gl_surface_t *dst = _dst; - cairo_bool_t has_component_alpha; - - TRACE ((stderr, "%s\n", __FUNCTION__)); - - /* If any of the glyphs require component alpha, we have to go through - * a mask, since only _cairo_gl_surface_composite() currently supports - * component alpha. - */ - if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER && - (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL || - info->font->options.antialias == CAIRO_ANTIALIAS_BEST)) - { - info->use_mask = TRUE; - } - - if (info->use_mask) { - return render_glyphs_via_mask (dst, dst_x, dst_y, - op, _src, info, clip); - } else { - return render_glyphs (dst, dst_x, dst_y, - op, _src, info, - &has_component_alpha, - clip); - } - -} - -cairo_int_status_t -_cairo_gl_composite_glyphs (void *_dst, - cairo_operator_t op, - cairo_surface_t *_src, - int src_x, - int src_y, - int dst_x, - int dst_y, - cairo_composite_glyphs_info_t *info) -{ - return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y, - dst_x, dst_y, info, NULL); -} - -void -_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_init (&cache->rtree, - GLYPH_CACHE_WIDTH, - GLYPH_CACHE_HEIGHT, - GLYPH_CACHE_MIN_SIZE, - sizeof (cairo_gl_glyph_t), - _cairo_gl_node_destroy); -} - -void -_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, - cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_fini (&cache->rtree); - cairo_surface_destroy (&cache->surface->base); -} diff --git a/src/cairo-gl-gradient-private.h b/src/cairo-gl-gradient-private.h deleted file mode 100644 index 0d9f41f54..000000000 --- a/src/cairo-gl-gradient-private.h +++ /dev/null @@ -1,96 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#ifndef CAIRO_GL_GRADIENT_PRIVATE_H -#define CAIRO_GL_GRADIENT_PRIVATE_H - -#define GL_GLEXT_PROTOTYPES - -#include "cairo-cache-private.h" -#include "cairo-device-private.h" -#include "cairo-reference-count-private.h" -#include "cairo-pattern-private.h" -#include "cairo-types-private.h" - -#include "cairo-gl.h" - -#if CAIRO_HAS_GLESV3_SURFACE -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> -#elif CAIRO_HAS_GLESV2_SURFACE -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#elif CAIRO_HAS_GL_SURFACE -#include <GL/gl.h> -#include <GL/glext.h> -#endif - -#define CAIRO_GL_GRADIENT_CACHE_SIZE 4096 - -/* XXX: Declare in a better place */ -typedef struct _cairo_gl_context cairo_gl_context_t; - -typedef struct _cairo_gl_gradient { - cairo_cache_entry_t cache_entry; - cairo_reference_count_t ref_count; - cairo_device_t *device; /* NB: we don't hold a reference */ - GLuint tex; - unsigned int n_stops; - const cairo_gradient_stop_t *stops; - cairo_gradient_stop_t stops_embedded[1]; -} cairo_gl_gradient_t; - -cairo_private cairo_int_status_t -_cairo_gl_gradient_create (cairo_gl_context_t *ctx, - unsigned int n_stops, - const cairo_gradient_stop_t *stops, - cairo_gl_gradient_t **gradient_out); - -cairo_private_no_warn cairo_gl_gradient_t * -_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient); - -cairo_private void -_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient); - -cairo_private cairo_bool_t -_cairo_gl_gradient_equal (const void *key_a, const void *key_b); - - -#endif /* CAIRO_GL_GRADIENT_PRIVATE_H */ diff --git a/src/cairo-gl-gradient.c b/src/cairo-gl-gradient.c deleted file mode 100644 index 293d4e30e..000000000 --- a/src/cairo-gl-gradient.c +++ /dev/null @@ -1,339 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#include "cairoint.h" -#include <stdint.h> -#include "cairo-error-private.h" -#include "cairo-gl-gradient-private.h" -#include "cairo-gl-private.h" - - -static int -_cairo_gl_gradient_sample_width (unsigned int n_stops, - const cairo_gradient_stop_t *stops) -{ - unsigned int n; - int width; - - width = 8; - for (n = 1; n < n_stops; n++) { - double dx = stops[n].offset - stops[n-1].offset; - double delta, max; - int ramp; - - if (dx == 0) - return 1024; /* we need to emulate an infinitely sharp step */ - - max = fabs (stops[n].color.red - stops[n-1].color.red); - - delta = fabs (stops[n].color.green - stops[n-1].color.green); - if (delta > max) - max = delta; - - delta = fabs (stops[n].color.blue - stops[n-1].color.blue); - if (delta > max) - max = delta; - - delta = fabs (stops[n].color.alpha - stops[n-1].color.alpha); - if (delta > max) - max = delta; - - ramp = 128 * max / dx; - if (ramp > width) - width = ramp; - } - - return (width + 7) & -8; -} - -static uint8_t premultiply(double c, double a) -{ - int v = c * a * 256; - return v - (v >> 8); -} - -static uint32_t color_stop_to_pixel(const cairo_gradient_stop_t *stop) -{ - uint8_t a, r, g, b; - - a = stop->color.alpha_short >> 8; - r = premultiply(stop->color.red, stop->color.alpha); - g = premultiply(stop->color.green, stop->color.alpha); - b = premultiply(stop->color.blue, stop->color.alpha); - - if (_cairo_is_little_endian ()) - return (uint32_t)a << 24 | r << 16 | g << 8 | b << 0; - else - return a << 0 | r << 8 | g << 16 | (uint32_t)b << 24; -} - -static cairo_status_t -_cairo_gl_gradient_render (const cairo_gl_context_t *ctx, - unsigned int n_stops, - const cairo_gradient_stop_t *stops, - void *bytes, - int width) -{ - pixman_image_t *gradient, *image; - pixman_gradient_stop_t pixman_stops_stack[32]; - pixman_gradient_stop_t *pixman_stops; - pixman_point_fixed_t p1, p2; - unsigned int i; - pixman_format_code_t gradient_pixman_format; - - /* - * Ensure that the order of the gradient's components in memory is BGRA. - * This is done so that the gradient's pixel data is always suitable for - * texture upload using format=GL_BGRA and type=GL_UNSIGNED_BYTE. - */ - if (_cairo_is_little_endian ()) - gradient_pixman_format = PIXMAN_a8r8g8b8; - else - gradient_pixman_format = PIXMAN_b8g8r8a8; - - pixman_stops = pixman_stops_stack; - if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) { - pixman_stops = _cairo_malloc_ab (n_stops, - sizeof (pixman_gradient_stop_t)); - if (unlikely (pixman_stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < n_stops; i++) { - pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset); - pixman_stops[i].color.red = stops[i].color.red_short; - pixman_stops[i].color.green = stops[i].color.green_short; - pixman_stops[i].color.blue = stops[i].color.blue_short; - pixman_stops[i].color.alpha = stops[i].color.alpha_short; - } - - p1.x = _cairo_fixed_16_16_from_double (0.5); - p1.y = 0; - p2.x = _cairo_fixed_16_16_from_double (width - 0.5); - p2.y = 0; - - gradient = pixman_image_create_linear_gradient (&p1, &p2, - pixman_stops, - n_stops); - if (pixman_stops != pixman_stops_stack) - free (pixman_stops); - - if (unlikely (gradient == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0); - pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD); - - image = pixman_image_create_bits (gradient_pixman_format, width, 1, - bytes, sizeof(uint32_t)*width); - if (unlikely (image == NULL)) { - pixman_image_unref (gradient); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - gradient, NULL, image, - 0, 0, - 0, 0, - 0, 0, - width, 1); - - pixman_image_unref (gradient); - pixman_image_unref (image); - - /* We need to fudge pixel 0 to hold the left-most color stop and not - * the neareset stop to the zeroth pixel centre in order to correctly - * populate the border color. For completeness, do both edges. - */ - ((uint32_t*)bytes)[0] = color_stop_to_pixel(&stops[0]); - ((uint32_t*)bytes)[width-1] = color_stop_to_pixel(&stops[n_stops-1]); - - return CAIRO_STATUS_SUCCESS; -} - -static uintptr_t -_cairo_gl_gradient_hash (unsigned int n_stops, - const cairo_gradient_stop_t *stops) -{ - return _cairo_hash_bytes (n_stops, - stops, - sizeof (cairo_gradient_stop_t) * n_stops); -} - -static cairo_gl_gradient_t * -_cairo_gl_gradient_lookup (cairo_gl_context_t *ctx, - uintptr_t hash, - unsigned int n_stops, - const cairo_gradient_stop_t *stops) -{ - cairo_gl_gradient_t lookup; - - lookup.cache_entry.hash = hash, - lookup.n_stops = n_stops; - lookup.stops = stops; - - return _cairo_cache_lookup (&ctx->gradients, &lookup.cache_entry); -} - -cairo_bool_t -_cairo_gl_gradient_equal (const void *key_a, const void *key_b) -{ - const cairo_gl_gradient_t *a = key_a; - const cairo_gl_gradient_t *b = key_b; - - if (a->n_stops != b->n_stops) - return FALSE; - - return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0; -} - -cairo_int_status_t -_cairo_gl_gradient_create (cairo_gl_context_t *ctx, - unsigned int n_stops, - const cairo_gradient_stop_t *stops, - cairo_gl_gradient_t **gradient_out) -{ - uintptr_t hash; - cairo_gl_gradient_t *gradient; - cairo_status_t status; - int tex_width; - GLint internal_format; - void *data; - - if ((unsigned int) ctx->max_texture_size / 2 <= n_stops) - return CAIRO_INT_STATUS_UNSUPPORTED; - - hash = _cairo_gl_gradient_hash (n_stops, stops); - - gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops); - if (gradient) { - *gradient_out = _cairo_gl_gradient_reference (gradient); - return CAIRO_STATUS_SUCCESS; - } - - gradient = _cairo_malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1)); - if (gradient == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - tex_width = _cairo_gl_gradient_sample_width (n_stops, stops); - if (tex_width > ctx->max_texture_size) - tex_width = ctx->max_texture_size; - - CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2); - gradient->cache_entry.hash = hash; - gradient->cache_entry.size = tex_width; - gradient->device = &ctx->base; - gradient->n_stops = n_stops; - gradient->stops = gradient->stops_embedded; - memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t)); - - glGenTextures (1, &gradient->tex); - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); - glBindTexture (ctx->tex_target, gradient->tex); - - data = _cairo_malloc_ab (tex_width, sizeof (uint32_t)); - if (unlikely (data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup_gradient; - } - - status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width); - if (unlikely (status)) - goto cleanup_data; - - /* - * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat' - * must match 'format' in glTexImage2D. - */ - if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES3 || - _cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) - internal_format = GL_BGRA; - else - internal_format = GL_RGBA; - - glTexImage2D (ctx->tex_target, 0, internal_format, tex_width, 1, 0, - GL_BGRA, GL_UNSIGNED_BYTE, data); - - free (data); - - /* we ignore errors here and just return an uncached gradient */ - if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry))) - CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1); - - *gradient_out = gradient; - return CAIRO_STATUS_SUCCESS; - -cleanup_data: - free (data); -cleanup_gradient: - free (gradient); - return status; -} - -cairo_gl_gradient_t * -_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient) -{ - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); - - _cairo_reference_count_inc (&gradient->ref_count); - - return gradient; -} - -void -_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient) -{ - cairo_gl_context_t *ctx; - cairo_status_t ignore; - - assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); - - if (! _cairo_reference_count_dec_and_test (&gradient->ref_count)) - return; - - if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) { - /* The gradient my still be active in the last operation, so flush */ - _cairo_gl_composite_flush (ctx); - glDeleteTextures (1, &gradient->tex); - ignore = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - } - - free (gradient); -} diff --git a/src/cairo-gl-info.c b/src/cairo-gl-info.c deleted file mode 100644 index c655b962e..000000000 --- a/src/cairo-gl-info.c +++ /dev/null @@ -1,147 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2010 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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. - * - * Contributor(s): - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - * Heiko Lewin <heiko.lewin@gmx.de> - */ - -#include "cairoint.h" -#include "cairo-gl-private.h" - -#include <errno.h> - -int -_cairo_gl_get_version (void) -{ - int major, minor; - const char *version = (const char *) glGetString (GL_VERSION); - const char *dot = version == NULL ? NULL : strchr (version, '.'); - const char *major_start = dot; - - /* Sanity check */ - if (dot == NULL || dot == version || *(dot + 1) == '\0') { - major = 0; - minor = 0; - } else { - /* Find the start of the major version in the string */ - while (major_start > version && *major_start != ' ') - --major_start; - major = strtol (major_start, NULL, 10); - minor = strtol (dot + 1, NULL, 10); - } - - return CAIRO_GL_VERSION_ENCODE (major, minor); -} - - -static cairo_gl_flavor_t -_cairo_gl_degrade_flavor_by_build_features (cairo_gl_flavor_t flavor) -{ - switch(flavor) { - case CAIRO_GL_FLAVOR_DESKTOP: -#if CAIRO_HAS_GL_SURFACE - return CAIRO_GL_FLAVOR_DESKTOP; -#else - return CAIRO_GL_FLAVOR_NONE; -#endif - - case CAIRO_GL_FLAVOR_ES3: -#if CAIRO_HAS_GLESV3_SURFACE - return CAIRO_GL_FLAVOR_ES3; -#else - /* intentional fall through: degrade to GLESv2 if GLESv3-surfaces are not available */ -#endif - - case CAIRO_GL_FLAVOR_ES2: -#if CAIRO_HAS_GLESV2_SURFACE - return CAIRO_GL_FLAVOR_ES2; -#else - /* intentional fall through: no OpenGL in first place or no surfaces for it's version */ -#endif - - case CAIRO_GL_FLAVOR_NONE: - default: - return CAIRO_GL_FLAVOR_NONE; - } -} - -cairo_gl_flavor_t -_cairo_gl_get_flavor (void) -{ - const char *version = (const char *) glGetString (GL_VERSION); - cairo_gl_flavor_t flavor; - - if (version == NULL) { - flavor = CAIRO_GL_FLAVOR_NONE; - } else if (strstr (version, "OpenGL ES 3") != NULL) { - flavor = CAIRO_GL_FLAVOR_ES3; - } else if (strstr (version, "OpenGL ES 2") != NULL) { - flavor = CAIRO_GL_FLAVOR_ES2; - } else { - flavor = CAIRO_GL_FLAVOR_DESKTOP; - } - - return _cairo_gl_degrade_flavor_by_build_features(flavor); -} - -unsigned long -_cairo_gl_get_vbo_size (void) -{ - unsigned long vbo_size; - - const char *env = getenv ("CAIRO_GL_VBO_SIZE"); - if (env == NULL) { - vbo_size = CAIRO_GL_VBO_SIZE_DEFAULT; - } else { - errno = 0; - vbo_size = strtol (env, NULL, 10); - assert (errno == 0); - assert (vbo_size > 0); - } - - return vbo_size; -} - -cairo_bool_t -_cairo_gl_has_extension (const char *ext) -{ - const char *extensions = (const char *) glGetString (GL_EXTENSIONS); - size_t len = strlen (ext); - const char *ext_ptr = extensions; - - if (unlikely (ext_ptr == NULL)) - return 0; - - while ((ext_ptr = strstr (ext_ptr, ext)) != NULL) { - if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0') - break; - ext_ptr += len; - } - - return (ext_ptr != NULL); -} diff --git a/src/cairo-gl-msaa-compositor.c b/src/cairo-gl-msaa-compositor.c deleted file mode 100644 index 7a83dd219..000000000 --- a/src/cairo-gl-msaa-compositor.c +++ /dev/null @@ -1,956 +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 © 2002 University of Southern California - * Copyright © 2005 Red Hat, Inc. - * Copyright © 2011 Intel Corporation - * Copyright © 2011 Samsung Electronics - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Henry Song <hsong@sisa.samsung.com> - * Martin Robinson <mrobinson@igalia.com> - */ - -#include "cairoint.h" - -#include "cairo-clip-inline.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-compositor-private.h" -#include "cairo-gl-private.h" -#include "cairo-path-private.h" -#include "cairo-traps-private.h" - -static cairo_bool_t -can_use_msaa_compositor (cairo_gl_surface_t *surface, - cairo_antialias_t antialias); - -static void -query_surface_capabilities (cairo_gl_surface_t *surface); - -struct _tristrip_composite_info { - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; -}; - -static cairo_int_status_t -_draw_trap (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_trapezoid_t *trap) -{ - cairo_point_t quad[4]; - - quad[0].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1, - &trap->left.p2, - trap->top); - quad[0].y = trap->top; - - quad[1].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1, - &trap->left.p2, - trap->bottom); - quad[1].y = trap->bottom; - - quad[2].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1, - &trap->right.p2, - trap->bottom); - quad[2].y = trap->bottom; - - quad[3].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1, - &trap->right.p2, - trap->top); - quad[3].y = trap->top; - return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); -} - -static cairo_int_status_t -_draw_traps (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_traps_t *traps) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - int i; - - for (i = 0; i < traps->num_traps; i++) { - cairo_trapezoid_t *trap = traps->traps + i; - if (unlikely ((status = _draw_trap (ctx, setup, trap)))) - return status; - } - - return status; -} - -static cairo_int_status_t -_draw_int_rect (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_rectangle_int_t *rect) -{ - cairo_box_t box; - cairo_point_t quad[4]; - - _cairo_box_from_rectangle (&box, rect); - quad[0].x = box.p1.x; - quad[0].y = box.p1.y; - quad[1].x = box.p1.x; - quad[1].y = box.p2.y; - quad[2].x = box.p2.x; - quad[2].y = box.p2.y; - quad[3].x = box.p2.x; - quad[3].y = box.p1.y; - - return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); -} - -static cairo_int_status_t -_draw_triangle_fan (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - const cairo_point_t *midpt, - const cairo_point_t *points, - int npoints) -{ - int i; - - /* Our strategy here is to not even try to build a triangle fan, but to - draw each triangle as if it was an unconnected member of a triangle strip. */ - for (i = 1; i < npoints; i++) { - cairo_int_status_t status; - cairo_point_t triangle[3]; - - triangle[0] = *midpt; - triangle[1] = points[i - 1]; - triangle[2] = points[i]; - - status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_clip_to_traps (cairo_clip_t *clip, - cairo_traps_t *traps) -{ - cairo_int_status_t status; - cairo_polygon_t polygon; - cairo_antialias_t antialias; - cairo_fill_rule_t fill_rule; - - _cairo_traps_init (traps); - - if (clip->num_boxes == 1 && clip->path == NULL) { - cairo_boxes_t boxes; - _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes); - return _cairo_traps_init_boxes (traps, &boxes); - } - - status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); - if (unlikely (status)) - return status; - - /* We ignore the antialias mode of the clip here, since the user requested - * unantialiased rendering of their path and we expect that this stencil - * based rendering of the clip to be a reasonable approximation to - * the intersection between that clip and the path. - * - * In other words, what the user expects when they try to perform - * a geometric intersection between an unantialiased polygon and an - * antialiased polygon is open to interpretation. And we choose the fast - * option. - */ - - _cairo_traps_init (traps); - status = _cairo_bentley_ottmann_tessellate_polygon (traps, - &polygon, - fill_rule); - _cairo_polygon_fini (&polygon); - - return status; -} - -cairo_int_status_t -_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_clip_t *clip) -{ - cairo_int_status_t status; - cairo_traps_t traps; - - status = _clip_to_traps (clip, &traps); - if (unlikely (status)) - return status; - status = _draw_traps (ctx, setup, &traps); - - _cairo_traps_fini (&traps); - return status; -} - -static cairo_bool_t -_should_use_unbounded_surface (cairo_composite_rectangles_t *composite) -{ - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - cairo_rectangle_int_t *source = &composite->source; - - if (composite->is_bounded) - return FALSE; - - /* This isn't just an optimization. It also detects when painting is used - to paint back the unbounded surface, preventing infinite recursion. */ - return ! (source->x <= 0 && source->y <= 0 && - source->height + source->y >= dst->height && - source->width + source->x >= dst->width); -} - -static cairo_surface_t* -_prepare_unbounded_surface (cairo_gl_surface_t *dst) -{ - - cairo_surface_t* surface = cairo_gl_surface_create (dst->base.device, - dst->base.content, - dst->width, - dst->height); - if (surface == NULL) - return NULL; - if (unlikely (surface->status)) { - cairo_surface_destroy (surface); - return NULL; - } - return surface; -} - -static cairo_int_status_t -_paint_back_unbounded_surface (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite, - cairo_surface_t *surface) -{ - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - cairo_int_status_t status; - - cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); - if (unlikely (pattern->status)) { - status = pattern->status; - goto finish; - } - - status = _cairo_compositor_paint (compositor, &dst->base, - composite->op, pattern, - composite->clip); - -finish: - cairo_pattern_destroy (pattern); - cairo_surface_destroy (surface); - return status; -} - -static cairo_bool_t -can_use_msaa_compositor (cairo_gl_surface_t *surface, - cairo_antialias_t antialias) -{ - cairo_gl_flavor_t gl_flavor = ((cairo_gl_context_t *) surface->base.device)->gl_flavor; - - query_surface_capabilities (surface); - if (! surface->supports_stencil) - return FALSE; - - /* Multisampling OpenGL ES surfaces only maintain one multisampling - framebuffer and thus must use the spans compositor to do non-antialiased - rendering. */ - if ((gl_flavor == CAIRO_GL_FLAVOR_ES3 || - gl_flavor == CAIRO_GL_FLAVOR_ES2) - && surface->supports_msaa - && surface->num_samples > 1 - && antialias == CAIRO_ANTIALIAS_NONE) - return FALSE; - - /* The MSAA compositor has a single-sample mode, so we can - support non-antialiased rendering. */ - if (antialias == CAIRO_ANTIALIAS_NONE) - return TRUE; - - if (antialias == CAIRO_ANTIALIAS_FAST || antialias == CAIRO_ANTIALIAS_DEFAULT) - return surface->supports_msaa; - return FALSE; -} - -static void -_cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite, - cairo_gl_composite_t *setup) -{ - if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip)) - return; - _cairo_gl_composite_set_clip (setup, composite->clip); -} - -/* Masking with the SOURCE operator requires two passes. In the first - * pass we use the mask as the source to get: - * result = (1 - ma) * dst - * In the second pass we use the add operator to achieve: - * result = (src * ma) + dst - * Combined this produces: - * result = (src * ma) + (1 - ma) * dst - */ -static cairo_int_status_t -_cairo_gl_msaa_compositor_mask_source_operator (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite) -{ - cairo_gl_composite_t setup; - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - cairo_gl_context_t *ctx = NULL; - cairo_int_status_t status; - - cairo_clip_t *clip = composite->clip; - cairo_traps_t traps; - - /* If we have a non-rectangular clip, we can avoid using the stencil buffer - * for clipping and just draw the clip polygon. */ - if (clip) { - status = _clip_to_traps (clip, &traps); - if (unlikely (status)) { - _cairo_traps_fini (&traps); - return status; - } - } - - status = _cairo_gl_composite_init (&setup, - CAIRO_OPERATOR_DEST_OUT, - dst, - FALSE /* assume_component_alpha */); - if (unlikely (status)) - return status; - status = _cairo_gl_composite_set_source (&setup, - &composite->mask_pattern.base, - &composite->mask_sample_area, - &composite->bounded, - FALSE); - if (unlikely (status)) - goto finish; - _cairo_gl_composite_set_multisample (&setup); - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto finish; - - if (! clip) - status = _draw_int_rect (ctx, &setup, &composite->bounded); - else - status = _draw_traps (ctx, &setup, &traps); - if (unlikely (status)) - goto finish; - - /* Now draw the second pass. */ - status = _cairo_gl_composite_set_operator (&setup, CAIRO_OPERATOR_ADD, - FALSE /* assume_component_alpha */); - if (unlikely (status)) - goto finish; - status = _cairo_gl_composite_set_source (&setup, - &composite->source_pattern.base, - &composite->source_sample_area, - &composite->bounded, - FALSE); - if (unlikely (status)) - goto finish; - status = _cairo_gl_composite_set_mask (&setup, - &composite->mask_pattern.base, - &composite->source_sample_area, - &composite->bounded, - FALSE); - if (unlikely (status)) - goto finish; - - _cairo_gl_context_set_destination (ctx, dst, setup.multisample); - - status = _cairo_gl_set_operands_and_operator (&setup, ctx); - if (unlikely (status)) - goto finish; - - if (! clip) - status = _draw_int_rect (ctx, &setup, &composite->bounded); - else - status = _draw_traps (ctx, &setup, &traps); - -finish: - _cairo_gl_composite_fini (&setup); - if (ctx) - status = _cairo_gl_context_release (ctx, status); - if (clip) - _cairo_traps_fini (&traps); - - return status; -} - -static cairo_int_status_t -_cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite) -{ - cairo_gl_composite_t setup; - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - cairo_gl_context_t *ctx = NULL; - cairo_int_status_t status; - cairo_operator_t op = composite->op; - cairo_clip_t *clip = composite->clip; - - if (! can_use_msaa_compositor (dst, CAIRO_ANTIALIAS_DEFAULT)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (composite->op == CAIRO_OPERATOR_CLEAR && - composite->original_mask_pattern != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* GL compositing operators cannot properly represent a mask operation - using the SOURCE compositing operator in one pass. This only matters if - there actually is a mask (there isn't in a paint operation) and if the - mask isn't totally opaque. */ - if (op == CAIRO_OPERATOR_SOURCE && - composite->original_mask_pattern != NULL && - ! _cairo_pattern_is_opaque (&composite->mask_pattern.base, - &composite->mask_sample_area)) { - - if (! _cairo_pattern_is_opaque (&composite->source_pattern.base, - &composite->source_sample_area)) { - return _cairo_gl_msaa_compositor_mask_source_operator (compositor, composite); - } - - /* If the source is opaque the operation reduces to OVER. */ - op = CAIRO_OPERATOR_OVER; - } - - if (_should_use_unbounded_surface (composite)) { - cairo_surface_t* surface = _prepare_unbounded_surface (dst); - - if (unlikely (surface == NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* This may be a paint operation. */ - if (composite->original_mask_pattern == NULL) { - status = _cairo_compositor_paint (compositor, surface, - CAIRO_OPERATOR_SOURCE, - &composite->source_pattern.base, - NULL); - } else { - status = _cairo_compositor_mask (compositor, surface, - CAIRO_OPERATOR_SOURCE, - &composite->source_pattern.base, - &composite->mask_pattern.base, - NULL); - } - - if (unlikely (status)) { - cairo_surface_destroy (surface); - return status; - } - - return _paint_back_unbounded_surface (compositor, composite, surface); - } - - status = _cairo_gl_composite_init (&setup, - op, - dst, - FALSE /* assume_component_alpha */); - if (unlikely (status)) - return status; - - status = _cairo_gl_composite_set_source (&setup, - &composite->source_pattern.base, - &composite->source_sample_area, - &composite->bounded, - FALSE); - if (unlikely (status)) - goto finish; - - if (composite->original_mask_pattern != NULL) { - status = _cairo_gl_composite_set_mask (&setup, - &composite->mask_pattern.base, - &composite->mask_sample_area, - &composite->bounded, - FALSE); - } - if (unlikely (status)) - goto finish; - - /* We always use multisampling here, because we do not yet have the smarts - to calculate when the clip or the source requires it. */ - _cairo_gl_composite_set_multisample (&setup); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto finish; - - if (! clip) - status = _draw_int_rect (ctx, &setup, &composite->bounded); - else - status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup, clip); - -finish: - _cairo_gl_composite_fini (&setup); - - if (ctx) - status = _cairo_gl_context_release (ctx, status); - - return status; -} - -static cairo_int_status_t -_cairo_gl_msaa_compositor_paint (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite) -{ - return _cairo_gl_msaa_compositor_mask (compositor, composite); -} - -static cairo_status_t -_stroke_shaper_add_triangle (void *closure, - const cairo_point_t triangle[3]) -{ - struct _tristrip_composite_info *info = closure; - return _cairo_gl_composite_emit_triangle_as_tristrip (info->ctx, - &info->setup, - triangle); -} - -static cairo_status_t -_stroke_shaper_add_triangle_fan (void *closure, - const cairo_point_t *midpoint, - const cairo_point_t *points, - int npoints) -{ - struct _tristrip_composite_info *info = closure; - return _draw_triangle_fan (info->ctx, &info->setup, - midpoint, points, npoints); -} - -static cairo_status_t -_stroke_shaper_add_quad (void *closure, - const cairo_point_t quad[4]) -{ - struct _tristrip_composite_info *info = closure; - return _cairo_gl_composite_emit_quad_as_tristrip (info->ctx, &info->setup, - quad); -} - -static cairo_int_status_t -_prevent_overlapping_strokes (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_composite_rectangles_t *composite, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm) -{ - cairo_rectangle_int_t stroke_extents; - - if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_cairo_pattern_is_opaque (&composite->source_pattern.base, - &composite->source_sample_area)) - return CAIRO_INT_STATUS_SUCCESS; - - if (glIsEnabled (GL_STENCIL_TEST) == FALSE) { - cairo_bool_t scissor_was_enabled; - - /* In case we have pending operations we have to flush before - adding the stencil buffer. */ - _cairo_gl_composite_flush (ctx); - - /* Enable the stencil buffer, even if we are not using it for clipping, - so we can use it below to prevent overlapping shapes. We initialize - it all to one here which represents infinite clip. */ - glDepthMask (GL_TRUE); - glEnable (GL_STENCIL_TEST); - - /* We scissor here so that we don't have to clear the entire stencil - * buffer. If the scissor test is already enabled, it was enabled - * for clipping. In that case, instead of calculating an intersection, - * we just reuse it, and risk clearing too much. */ - scissor_was_enabled = glIsEnabled (GL_SCISSOR_TEST); - if (! scissor_was_enabled) { - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, - FALSE, /* is_vector */ - &stroke_extents); - _cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents); - } - glClearStencil (1); - glClear (GL_STENCIL_BUFFER_BIT); - if (! scissor_was_enabled) - glDisable (GL_SCISSOR_TEST); - - glStencilFunc (GL_EQUAL, 1, 1); - } - - /* This means that once we draw to a particular pixel nothing else can - be drawn there until the stencil buffer is reset or the stencil test - is disabled. */ - glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO); - - _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); - setup->dst->clip_on_stencil_buffer = NULL; - - return CAIRO_INT_STATUS_SUCCESS; -} - -static void -query_surface_capabilities (cairo_gl_surface_t *surface) -{ - GLint samples, stencil_bits; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - /* Texture surfaces are create in such a way that they always - have stencil and multisample bits if possible, so we don't - need to query their capabilities lazily. */ - if (_cairo_gl_surface_is_texture (surface)) - return; - if (surface->stencil_and_msaa_caps_initialized) - return; - - surface->stencil_and_msaa_caps_initialized = TRUE; - surface->supports_stencil = FALSE; - surface->supports_msaa = FALSE; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return; - - _cairo_gl_context_set_destination (ctx, surface, FALSE); - - glGetIntegerv(GL_SAMPLES, &samples); - glGetIntegerv(GL_STENCIL_BITS, &stencil_bits); - surface->supports_stencil = stencil_bits > 0; - surface->supports_msaa = samples > 1; - surface->num_samples = samples; - - status = _cairo_gl_context_release (ctx, status); -} - -static cairo_int_status_t -_cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_int_status_t status; - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - struct _tristrip_composite_info info; - - if (! can_use_msaa_compositor (dst, antialias)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (composite->is_bounded == FALSE) { - cairo_surface_t* surface = _prepare_unbounded_surface (dst); - - if (unlikely (surface == NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_compositor_stroke (compositor, surface, - CAIRO_OPERATOR_SOURCE, - &composite->source_pattern.base, - path, style, ctm, ctm_inverse, - tolerance, antialias, NULL); - if (unlikely (status)) { - cairo_surface_destroy (surface); - return status; - } - - return _paint_back_unbounded_surface (compositor, composite, surface); - } - - status = _cairo_gl_composite_init (&info.setup, - composite->op, - dst, - FALSE /* assume_component_alpha */); - if (unlikely (status)) - return status; - - info.ctx = NULL; - - status = _cairo_gl_composite_set_source (&info.setup, - &composite->source_pattern.base, - &composite->source_sample_area, - &composite->bounded, - FALSE); - if (unlikely (status)) - goto finish; - - _cairo_gl_msaa_compositor_set_clip (composite, &info.setup); - if (antialias != CAIRO_ANTIALIAS_NONE) - _cairo_gl_composite_set_multisample (&info.setup); - - status = _cairo_gl_composite_begin (&info.setup, &info.ctx); - if (unlikely (status)) - goto finish; - - status = _prevent_overlapping_strokes (info.ctx, &info.setup, - composite, path, style, ctm); - if (unlikely (status)) - goto finish; - - status = _cairo_path_fixed_stroke_to_shaper ((cairo_path_fixed_t *) path, - style, - ctm, - ctm_inverse, - tolerance, - _stroke_shaper_add_triangle, - _stroke_shaper_add_triangle_fan, - _stroke_shaper_add_quad, - &info); - if (unlikely (status)) - goto finish; - -finish: - _cairo_gl_composite_fini (&info.setup); - - if (info.ctx) - status = _cairo_gl_context_release (info.ctx, status); - - return status; -} - -static cairo_int_status_t -_draw_simple_quad_path (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - const cairo_path_fixed_t *path) -{ - cairo_point_t triangle[3]; - cairo_int_status_t status; - const cairo_point_t *points; - - points = cairo_path_head (path)->points; - triangle[0] = points[0]; - triangle[1] = points[1]; - triangle[2] = points[2]; - status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); - if (status) - return status; - - triangle[0] = points[2]; - triangle[1] = points[3]; - triangle[2] = points[0]; - return _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); -} - -static cairo_int_status_t -_cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - cairo_gl_composite_t setup; - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - cairo_gl_context_t *ctx = NULL; - cairo_int_status_t status; - cairo_traps_t traps; - cairo_bool_t draw_path_with_traps; - - if (! can_use_msaa_compositor (dst, antialias)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (composite->is_bounded == FALSE) { - cairo_surface_t* surface = _prepare_unbounded_surface (dst); - - if (unlikely (surface == NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - - status = _cairo_compositor_fill (compositor, surface, - CAIRO_OPERATOR_SOURCE, - &composite->source_pattern.base, - path, fill_rule, tolerance, - antialias, NULL); - - if (unlikely (status)) { - cairo_surface_destroy (surface); - return status; - } - - return _paint_back_unbounded_surface (compositor, composite, surface); - } - - draw_path_with_traps = ! _cairo_path_fixed_is_simple_quad (path); - - if (draw_path_with_traps) { - _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps); - if (unlikely (status)) - goto cleanup_traps; - } - - status = _cairo_gl_composite_init (&setup, - composite->op, - dst, - FALSE /* assume_component_alpha */); - if (unlikely (status)) - goto cleanup_traps; - - status = _cairo_gl_composite_set_source (&setup, - &composite->source_pattern.base, - &composite->source_sample_area, - &composite->bounded, - FALSE); - if (unlikely (status)) - goto cleanup_setup; - - _cairo_gl_msaa_compositor_set_clip (composite, &setup); - if (antialias != CAIRO_ANTIALIAS_NONE) - _cairo_gl_composite_set_multisample (&setup); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto cleanup_setup; - - if (! draw_path_with_traps) - status = _draw_simple_quad_path (ctx, &setup, path); - else - status = _draw_traps (ctx, &setup, &traps); - if (unlikely (status)) - goto cleanup_setup; - -cleanup_setup: - _cairo_gl_composite_fini (&setup); - - if (ctx) - status = _cairo_gl_context_release (ctx, status); - -cleanup_traps: - if (draw_path_with_traps) - _cairo_traps_fini (&traps); - - return status; -} - -static cairo_int_status_t -_cairo_gl_msaa_compositor_glyphs (const cairo_compositor_t *compositor, - cairo_composite_rectangles_t *composite, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_bool_t overlap) -{ - cairo_int_status_t status; - cairo_surface_t *src = NULL; - int src_x, src_y; - cairo_composite_glyphs_info_t info; - - cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; - - query_surface_capabilities (dst); - if (! dst->supports_stencil) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (composite->op == CAIRO_OPERATOR_CLEAR) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (composite->is_bounded == FALSE) { - cairo_surface_t* surface = _prepare_unbounded_surface (dst); - - if (unlikely (surface == NULL)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_compositor_glyphs (compositor, surface, - CAIRO_OPERATOR_SOURCE, - &composite->source_pattern.base, - glyphs, num_glyphs, - scaled_font, composite->clip); - - if (unlikely (status)) { - cairo_surface_destroy (surface); - return status; - } - - return _paint_back_unbounded_surface (compositor, composite, surface); - } - - src = _cairo_gl_pattern_to_source (&dst->base, - &composite->source_pattern.base, - FALSE, - &composite->bounded, - &composite->source_sample_area, - &src_x, &src_y); - if (unlikely (src->status)) { - status = src->status; - goto finish; - } - - status = _cairo_gl_check_composite_glyphs (composite, - scaled_font, glyphs, - &num_glyphs); - if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) - goto finish; - - info.font = scaled_font; - info.glyphs = glyphs; - info.num_glyphs = num_glyphs; - info.use_mask = overlap || ! composite->is_bounded || - composite->op == CAIRO_OPERATOR_SOURCE; - info.extents = composite->bounded; - - _cairo_scaled_font_freeze_cache (scaled_font); - status = _cairo_gl_composite_glyphs_with_clip (dst, composite->op, - src, src_x, src_y, - 0, 0, &info, - composite->clip); - - _cairo_scaled_font_thaw_cache (scaled_font); - -finish: - if (src) - cairo_surface_destroy (src); - - return status; -} - -static void -_cairo_gl_msaa_compositor_init (cairo_compositor_t *compositor, - const cairo_compositor_t *delegate) -{ - compositor->delegate = delegate; - - compositor->paint = _cairo_gl_msaa_compositor_paint; - compositor->mask = _cairo_gl_msaa_compositor_mask; - compositor->fill = _cairo_gl_msaa_compositor_fill; - compositor->stroke = _cairo_gl_msaa_compositor_stroke; - compositor->glyphs = _cairo_gl_msaa_compositor_glyphs; -} - -const cairo_compositor_t * -_cairo_gl_msaa_compositor_get (void) -{ - static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; - static cairo_compositor_t compositor; - if (_cairo_atomic_init_once_enter(&once)) { - _cairo_gl_msaa_compositor_init (&compositor, - _cairo_gl_span_compositor_get ()); - _cairo_atomic_init_once_leave(&once); - } - - return &compositor; -} diff --git a/src/cairo-gl-operand.c b/src/cairo-gl-operand.c deleted file mode 100644 index a754bde2f..000000000 --- a/src/cairo-gl-operand.c +++ /dev/null @@ -1,793 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 Red Hat, Inc - * Copyright © 2011 Intel 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-compositor-private.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" -#include "cairo-image-surface-private.h" -#include "cairo-surface-backend-private.h" -#include "cairo-surface-offset-private.h" -#include "cairo-surface-subsurface-inline.h" - -static cairo_int_status_t -_cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst, - const cairo_gradient_pattern_t *pattern, - cairo_gl_gradient_t **gradient) -{ - cairo_gl_context_t *ctx; - cairo_status_t status; - - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient); - - return _cairo_gl_context_release (ctx, status); -} - -static cairo_status_t -_cairo_gl_subsurface_clone_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *_src, - cairo_gl_surface_t *dst, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen) -{ - const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src; - cairo_surface_pattern_t local_pattern; - cairo_surface_subsurface_t *sub; - cairo_gl_surface_t *surface; - cairo_gl_context_t *ctx; - cairo_surface_attributes_t *attributes; - cairo_status_t status; - - sub = (cairo_surface_subsurface_t *) src->surface; - - if (sub->snapshot && - sub->snapshot->type == CAIRO_SURFACE_TYPE_GL && - sub->snapshot->device == dst->base.device) - { - surface = (cairo_gl_surface_t *) - cairo_surface_reference (sub->snapshot); - } - else - { - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - /* XXX Trim surface to the sample area within the subsurface? */ - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch (ctx, - sub->target->content, - sub->extents.width, - sub->extents.height); - if (surface->base.status) - return _cairo_gl_context_release (ctx, surface->base.status); - - _cairo_pattern_init_for_surface (&local_pattern, sub->target); - cairo_matrix_init_translate (&local_pattern.base.matrix, - sub->extents.x, sub->extents.y); - local_pattern.base.filter = CAIRO_FILTER_NEAREST; - status = _cairo_surface_paint (&surface->base, - CAIRO_OPERATOR_SOURCE, - &local_pattern.base, - NULL); - _cairo_pattern_fini (&local_pattern.base); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (&surface->base); - return status; - } - - _cairo_surface_subsurface_set_snapshot (&sub->base, &surface->base); - } - - status = _cairo_gl_surface_resolve_multisampling (surface); - if (unlikely (status)) - return status; - - attributes = &operand->texture.attributes; - - operand->type = CAIRO_GL_OPERAND_TEXTURE; - operand->texture.surface = surface; - operand->texture.owns_surface = surface; - operand->texture.tex = surface->tex; - - if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) { - attributes->matrix = src->base.matrix; - } else { - cairo_matrix_t m; - - cairo_matrix_init_scale (&m, - 1.0 / surface->width, - 1.0 / surface->height); - cairo_matrix_multiply (&attributes->matrix, &src->base.matrix, &m); - } - - attributes->extend = src->base.extend; - attributes->filter = src->base.filter; - attributes->has_component_alpha = src->base.has_component_alpha; - - operand->texture.texgen = use_texgen; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_subsurface_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *_src, - cairo_gl_surface_t *dst, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen) -{ - const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src; - cairo_surface_subsurface_t *sub; - cairo_gl_surface_t *surface; - cairo_surface_attributes_t *attributes; - cairo_int_status_t status; - - sub = (cairo_surface_subsurface_t *) src->surface; - - if (sample->x < 0 || sample->y < 0 || - sample->x + sample->width > sub->extents.width || - sample->y + sample->height > sub->extents.height) - { - return _cairo_gl_subsurface_clone_operand_init (operand, _src, - dst, sample, extents, - use_texgen); - } - - surface = (cairo_gl_surface_t *) sub->target; - if (surface->base.device && surface->base.device != dst->base.device) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_gl_surface_is_texture (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_gl_surface_resolve_multisampling (surface); - if (unlikely (status)) - return status; - - /* Translate the matrix from - * (unnormalized src -> unnormalized src) to - * (unnormalized dst -> unnormalized src) - */ - _cairo_gl_operand_copy(operand, &surface->operand); - - attributes = &operand->texture.attributes; - attributes->matrix = src->base.matrix; - attributes->matrix.x0 += sub->extents.x; - attributes->matrix.y0 += sub->extents.y; - cairo_matrix_multiply (&attributes->matrix, - &attributes->matrix, - &surface->operand.texture.attributes.matrix); - - attributes->extend = src->base.extend; - attributes->filter = src->base.filter; - attributes->has_component_alpha = src->base.has_component_alpha; - - operand->texture.texgen = use_texgen; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_surface_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *_src, - cairo_gl_surface_t *dst, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen) -{ - const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src; - cairo_gl_surface_t *surface; - cairo_surface_attributes_t *attributes; - cairo_int_status_t status; - - surface = (cairo_gl_surface_t *) src->surface; - if (surface->base.type != CAIRO_SURFACE_TYPE_GL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface->base.backend->type != CAIRO_SURFACE_TYPE_GL) { - if (_cairo_surface_is_subsurface (&surface->base)) - return _cairo_gl_subsurface_operand_init (operand, _src, dst, - sample, extents, - use_texgen); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (surface->base.device && surface->base.device != dst->base.device) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface->base.device && ! _cairo_gl_surface_is_texture (surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_gl_surface_resolve_multisampling (surface); - if (unlikely (status)) - return status; - - _cairo_gl_operand_copy(operand, &surface->operand); - - attributes = &operand->texture.attributes; - cairo_matrix_multiply (&attributes->matrix, - &src->base.matrix, - &attributes->matrix); - - attributes->extend = src->base.extend; - attributes->filter = src->base.filter; - attributes->has_component_alpha = src->base.has_component_alpha; - - operand->texture.texgen = use_texgen; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand, - const cairo_pattern_t *_src, - cairo_gl_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_gl_surface_t *surface; - cairo_gl_context_t *ctx; - cairo_image_surface_t *image; - cairo_bool_t src_is_gl_surface = FALSE; - cairo_rectangle_int_t map_extents; - - if (_src->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_t* src_surface = ((cairo_surface_pattern_t *) _src)->surface; - src_is_gl_surface = src_surface->type == CAIRO_SURFACE_TYPE_GL; - } - - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch (ctx, - CAIRO_CONTENT_COLOR_ALPHA, - extents->width, extents->height); - map_extents = *extents; - map_extents.x = map_extents.y = 0; - image = _cairo_surface_map_to_image (&surface->base, &map_extents); - - /* If the pattern is a GL surface, it belongs to some other GL context, - so we need to release this device while we paint it to the image. */ - if (src_is_gl_surface) { - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - _cairo_surface_unmap_image (&surface->base, image); - goto fail; - } - } - - status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y, - CAIRO_OPERATOR_SOURCE, _src, NULL); - - if (src_is_gl_surface) { - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) { - _cairo_surface_unmap_image (&surface->base, image); - goto fail; - } - } - - status = _cairo_surface_unmap_image (&surface->base, image); - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) - goto fail; - - *operand = surface->operand; - operand->texture.owns_surface = surface; - operand->texture.attributes.matrix.x0 -= extents->x * operand->texture.attributes.matrix.xx; - operand->texture.attributes.matrix.y0 -= extents->y * operand->texture.attributes.matrix.yy; - return CAIRO_STATUS_SUCCESS; - -fail: - cairo_surface_destroy (&surface->base); - return status; -} - -void -_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand, - const cairo_color_t *color) -{ - operand->type = CAIRO_GL_OPERAND_CONSTANT; - operand->constant.color[0] = color->red * color->alpha; - operand->constant.color[1] = color->green * color->alpha; - operand->constant.color[2] = color->blue * color->alpha; - operand->constant.color[3] = color->alpha; -} - -void -_cairo_gl_operand_translate (cairo_gl_operand_t *operand, - double tx, double ty) -{ - switch (operand->type) { - case CAIRO_GL_OPERAND_TEXTURE: - operand->texture.attributes.matrix.x0 -= tx * operand->texture.attributes.matrix.xx; - operand->texture.attributes.matrix.y0 -= ty * operand->texture.attributes.matrix.yy; - break; - - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - operand->gradient.m.x0 -= tx * operand->gradient.m.xx; - operand->gradient.m.y0 -= ty * operand->gradient.m.yy; - break; - - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - case CAIRO_GL_OPERAND_COUNT: - default: - break; - } -} - -static cairo_status_t -_cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *pattern, - cairo_gl_surface_t *dst, - cairo_bool_t use_texgen) -{ - const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern; - cairo_status_t status; - - assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || - gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); - - if (! _cairo_gl_device_has_glsl (dst->base.device)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_gl_create_gradient_texture (dst, - gradient, - &operand->gradient.gradient); - if (unlikely (status)) - return status; - - if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; - double x0, y0, dx, dy, sf, offset; - - dx = linear->pd2.x - linear->pd1.x; - dy = linear->pd2.y - linear->pd1.y; - sf = 1.0 / (dx * dx + dy * dy); - dx *= sf; - dy *= sf; - - x0 = linear->pd1.x; - y0 = linear->pd1.y; - offset = dx * x0 + dy * y0; - - operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT; - - cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0); - if (! _cairo_matrix_is_identity (&pattern->matrix)) { - cairo_matrix_multiply (&operand->gradient.m, - &pattern->matrix, - &operand->gradient.m); - } - } else { - cairo_matrix_t m; - cairo_circle_double_t circles[2]; - double x0, y0, r0, dx, dy, dr; - - /* - * Some fragment shader implementations use half-floats to - * represent numbers, so the maximum number they can represent - * is about 2^14. Some intermediate computations used in the - * radial gradient shaders can produce results of up to 2*k^4. - * Setting k=8 makes the maximum result about 8192 (assuming - * that the extreme circles are not much smaller than the - * destination image). - */ - _cairo_gradient_pattern_fit_to_range (gradient, 8., - &operand->gradient.m, circles); - - x0 = circles[0].center.x; - y0 = circles[0].center.y; - r0 = circles[0].radius; - dx = circles[1].center.x - x0; - dy = circles[1].center.y - y0; - dr = circles[1].radius - r0; - - operand->gradient.a = dx * dx + dy * dy - dr * dr; - operand->gradient.radius_0 = r0; - operand->gradient.circle_d.center.x = dx; - operand->gradient.circle_d.center.y = dy; - operand->gradient.circle_d.radius = dr; - - if (operand->gradient.a == 0) - operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0; - else if (pattern->extend == CAIRO_EXTEND_NONE) - operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE; - else - operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT; - - cairo_matrix_init_translate (&m, -x0, -y0); - cairo_matrix_multiply (&operand->gradient.m, - &operand->gradient.m, - &m); - } - - operand->gradient.extend = pattern->extend; - operand->gradient.texgen = use_texgen; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gl_operand_copy (cairo_gl_operand_t *dst, - const cairo_gl_operand_t *src) -{ - *dst = *src; - switch (dst->type) { - case CAIRO_GL_OPERAND_CONSTANT: - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - _cairo_gl_gradient_reference (dst->gradient.gradient); - break; - case CAIRO_GL_OPERAND_TEXTURE: - cairo_surface_reference (&dst->texture.owns_surface->base); - break; - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - break; - } -} - -void -_cairo_gl_operand_destroy (cairo_gl_operand_t *operand) -{ - switch (operand->type) { - case CAIRO_GL_OPERAND_CONSTANT: - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - _cairo_gl_gradient_destroy (operand->gradient.gradient); - break; - case CAIRO_GL_OPERAND_TEXTURE: - cairo_surface_destroy (&operand->texture.owns_surface->base); - break; - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - break; - } - - operand->type = CAIRO_GL_OPERAND_NONE; -} - -cairo_int_status_t -_cairo_gl_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *pattern, - cairo_gl_surface_t *dst, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen) -{ - cairo_int_status_t status; - - TRACE ((stderr, "%s: type=%d\n", __FUNCTION__, pattern->type)); - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - _cairo_gl_solid_operand_init (operand, - &((cairo_solid_pattern_t *) pattern)->color); - return CAIRO_STATUS_SUCCESS; - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_gl_surface_operand_init (operand, pattern, dst, - sample, extents, use_texgen); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - break; - - return status; - - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_gl_gradient_operand_init (operand, pattern, dst, - use_texgen); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - break; - - return status; - - default: - case CAIRO_PATTERN_TYPE_MESH: - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - break; - } - - return _cairo_gl_pattern_texture_setup (operand, pattern, dst, extents); -} - -cairo_filter_t -_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand) -{ - cairo_filter_t filter; - - switch ((int) operand->type) { - case CAIRO_GL_OPERAND_TEXTURE: - filter = operand->texture.attributes.filter; - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - filter = CAIRO_FILTER_BILINEAR; - break; - default: - filter = CAIRO_FILTER_DEFAULT; - break; - } - - return filter; -} - -GLint -_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand) -{ - cairo_filter_t filter = _cairo_gl_operand_get_filter (operand); - - return filter != CAIRO_FILTER_FAST && filter != CAIRO_FILTER_NEAREST ? - GL_LINEAR : - GL_NEAREST; -} - -cairo_extend_t -_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand) -{ - cairo_extend_t extend; - - switch ((int) operand->type) { - case CAIRO_GL_OPERAND_TEXTURE: - extend = operand->texture.attributes.extend; - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - extend = operand->gradient.extend; - break; - default: - extend = CAIRO_EXTEND_NONE; - break; - } - - return extend; -} - - -void -_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, - cairo_gl_operand_t *operand, - cairo_gl_tex_t tex_unit) -{ - const cairo_matrix_t *texgen = NULL; - - switch (operand->type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - return; - - case CAIRO_GL_OPERAND_CONSTANT: - _cairo_gl_shader_bind_vec4 (ctx, - ctx->current_shader->constant_location[tex_unit], - operand->constant.color[0], - operand->constant.color[1], - operand->constant.color[2], - operand->constant.color[3]); - return; - - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - _cairo_gl_shader_bind_float (ctx, - ctx->current_shader->a_location[tex_unit], - operand->gradient.a); - /* fall through */ - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - _cairo_gl_shader_bind_vec3 (ctx, - ctx->current_shader->circle_d_location[tex_unit], - operand->gradient.circle_d.center.x, - operand->gradient.circle_d.center.y, - operand->gradient.circle_d.radius); - _cairo_gl_shader_bind_float (ctx, - ctx->current_shader->radius_0_location[tex_unit], - operand->gradient.radius_0); - /* fall through */ - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_TEXTURE: - /* - * For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used - * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled, - * these shaders need the texture dimensions for their calculations. - */ - if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && - _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE && - _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR) - { - float width, height; - if (operand->type == CAIRO_GL_OPERAND_TEXTURE) { - width = operand->texture.surface->width; - height = operand->texture.surface->height; - } - else { - width = operand->gradient.gradient->cache_entry.size, - height = 1; - } - _cairo_gl_shader_bind_vec2 (ctx, - ctx->current_shader->texdims_location[tex_unit], - width, height); - } - break; - } - - if (operand->type == CAIRO_GL_OPERAND_TEXTURE) { - if (operand->texture.texgen) - texgen = &operand->texture.attributes.matrix; - } else { - if (operand->gradient.texgen) - texgen = &operand->gradient.m; - } - if (texgen) { - _cairo_gl_shader_bind_matrix(ctx, - ctx->current_shader->texgen_location[tex_unit], - texgen); - } -} - - -cairo_bool_t -_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest, - cairo_gl_operand_t *source, - unsigned int vertex_offset) -{ - if (dest->type != source->type) - return TRUE; - if (dest->vertex_offset != vertex_offset) - return TRUE; - - switch (source->type) { - case CAIRO_GL_OPERAND_NONE: - return FALSE; - case CAIRO_GL_OPERAND_CONSTANT: - return dest->constant.color[0] != source->constant.color[0] || - dest->constant.color[1] != source->constant.color[1] || - dest->constant.color[2] != source->constant.color[2] || - dest->constant.color[3] != source->constant.color[3]; - case CAIRO_GL_OPERAND_TEXTURE: - return dest->texture.surface != source->texture.surface || - dest->texture.attributes.extend != source->texture.attributes.extend || - dest->texture.attributes.filter != source->texture.attributes.filter || - dest->texture.attributes.has_component_alpha != source->texture.attributes.has_component_alpha; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - /* XXX: improve this */ - return TRUE; - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - break; - } - return TRUE; -} - -unsigned int -_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand) -{ - switch (operand->type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - return 0; - case CAIRO_GL_OPERAND_TEXTURE: - return operand->texture.texgen ? 0 : 2 * sizeof (GLfloat); - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - return operand->gradient.texgen ? 0 : 2 * sizeof (GLfloat); - } -} - -void -_cairo_gl_operand_emit (cairo_gl_operand_t *operand, - GLfloat ** vb, - GLfloat x, - GLfloat y) -{ - switch (operand->type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - if (! operand->gradient.texgen) { - double s = x; - double t = y; - - cairo_matrix_transform_point (&operand->gradient.m, &s, &t); - - *(*vb)++ = s; - *(*vb)++ = t; - } - break; - case CAIRO_GL_OPERAND_TEXTURE: - if (! operand->texture.texgen) { - cairo_surface_attributes_t *src_attributes = &operand->texture.attributes; - double s = x; - double t = y; - - cairo_matrix_transform_point (&src_attributes->matrix, &s, &t); - *(*vb)++ = s; - *(*vb)++ = t; - } - break; - } -} diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h deleted file mode 100644 index f02a58763..000000000 --- a/src/cairo-gl-private.h +++ /dev/null @@ -1,865 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 Red Hat, Inc - * Copyright © 2011 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - * T. Zachary Laine <whatwasthataddress@gmail.com> - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - */ - -#ifndef CAIRO_GL_PRIVATE_H -#define CAIRO_GL_PRIVATE_H - -#define GL_GLEXT_PROTOTYPES - -#include "cairoint.h" - -#include "cairo-gl.h" -#include "cairo-gl-gradient-private.h" - -#include "cairo-device-private.h" -#include "cairo-error-private.h" -#include "cairo-rtree-private.h" -#include "cairo-scaled-font-private.h" -#include "cairo-spans-compositor-private.h" -#include "cairo-array-private.h" - -#include <assert.h> - -#if CAIRO_HAS_GLESV3_SURFACE -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> -#elif CAIRO_HAS_GLESV2_SURFACE -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#elif CAIRO_HAS_GL_SURFACE -#include <GL/gl.h> -#include <GL/glext.h> -#endif - -#include "cairo-gl-ext-def-private.h" - -#define DEBUG_GL 0 - -#if DEBUG_GL && __GNUC__ -#define UNSUPPORTED(reason) ({ \ - fprintf (stderr, \ - "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \ - __FUNCTION__, __LINE__, reason); \ - CAIRO_INT_STATUS_UNSUPPORTED; \ -}) -#else -#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED -#endif - -#define CAIRO_GL_VERSION_ENCODE(major, minor) ( \ - ((major) * 256) \ - + ((minor) * 1)) - -/* maximal number of shaders we keep in the cache. - * Random number that is hopefully big enough to not cause many cache evictions. */ -#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64 - -/* VBO size that we allocate, smaller size means we gotta flush more often, - * but larger means hogging more memory and can cause trouble for drivers - * (especially on embedded devices). Use the CAIRO_GL_VBO_SIZE environment - * variable to set this to a different size. */ -#define CAIRO_GL_VBO_SIZE_DEFAULT (1024*1024) - -typedef struct _cairo_gl_surface cairo_gl_surface_t; - -/* GL flavor is the type of GL supported by the underlying platform. */ -typedef enum cairo_gl_flavor { - CAIRO_GL_FLAVOR_NONE = 0, - CAIRO_GL_FLAVOR_DESKTOP = 1, - CAIRO_GL_FLAVOR_ES2 = 2, - CAIRO_GL_FLAVOR_ES3 = 3 -} cairo_gl_flavor_t; - -/* Indices for vertex attributes used by BindAttribLocation, etc. */ -enum { - CAIRO_GL_VERTEX_ATTRIB_INDEX = 0, - CAIRO_GL_COLOR_ATTRIB_INDEX = 1, - CAIRO_GL_TEXCOORD0_ATTRIB_INDEX = 2, - CAIRO_GL_TEXCOORD1_ATTRIB_INDEX = CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + 1 -}; - -typedef enum cairo_gl_operand_type { - CAIRO_GL_OPERAND_NONE, - CAIRO_GL_OPERAND_CONSTANT, - CAIRO_GL_OPERAND_TEXTURE, - CAIRO_GL_OPERAND_LINEAR_GRADIENT, - CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0, - CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE, - CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT, - - CAIRO_GL_OPERAND_COUNT -} cairo_gl_operand_type_t; - -/* This union structure describes a potential source or mask operand to the - * compositing equation. - */ -typedef struct cairo_gl_operand { - cairo_gl_operand_type_t type; - union { - struct { - GLuint tex; - cairo_gl_surface_t *surface; - cairo_gl_surface_t *owns_surface; - cairo_surface_attributes_t attributes; - int texgen; - } texture; - struct { - GLfloat color[4]; - } constant; - struct { - cairo_gl_gradient_t *gradient; - cairo_matrix_t m; - cairo_circle_double_t circle_d; - double radius_0, a; - cairo_extend_t extend; - int texgen; - } gradient; - }; - unsigned int vertex_offset; -} cairo_gl_operand_t; - -typedef struct cairo_gl_source { - cairo_surface_t base; - cairo_gl_operand_t operand; -} cairo_gl_source_t; - -struct _cairo_gl_surface { - cairo_surface_t base; - cairo_gl_operand_t operand; - - int width, height; - - GLuint tex; /* GL texture object containing our data. */ - GLuint fb; /* GL framebuffer object wrapping our data. */ - GLuint depth_stencil; /* GL renderbuffer object for holding stencil buffer clip. */ - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE - GLuint msaa_rb; /* The ARB MSAA path uses a renderbuffer. */ - GLuint msaa_fb; -#endif - GLuint msaa_depth_stencil; - - cairo_bool_t stencil_and_msaa_caps_initialized; - cairo_bool_t supports_stencil; /* Stencil support for for non-texture surfaces. */ - cairo_bool_t supports_msaa; - GLint num_samples; - cairo_bool_t msaa_active; /* Whether the multisampling - framebuffer is active or not. */ - cairo_bool_t content_in_texture; /* whether we just uploaded image - to texture, used for certain - gles2 extensions and glesv3 */ - cairo_clip_t *clip_on_stencil_buffer; - - int owns_tex; - cairo_bool_t needs_update; - - cairo_region_t *clip_region; -}; - -typedef struct cairo_gl_glyph_cache { - cairo_rtree_t rtree; - cairo_gl_surface_t *surface; -} cairo_gl_glyph_cache_t; - -typedef enum cairo_gl_tex { - CAIRO_GL_TEX_SOURCE = 0, - CAIRO_GL_TEX_MASK = 1, - CAIRO_GL_TEX_TEMP = 2 -} cairo_gl_tex_t; - -typedef struct cairo_gl_shader { - GLuint fragment_shader; - GLuint program; - GLint mvp_location; - GLint constant_location[2]; - GLint a_location[2]; - GLint circle_d_location[2]; - GLint radius_0_location[2]; - GLint texdims_location[2]; - GLint texgen_location[2]; -} cairo_gl_shader_t; - -typedef enum cairo_gl_shader_in { - CAIRO_GL_SHADER_IN_NORMAL, - CAIRO_GL_SHADER_IN_CA_SOURCE, - CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, - - CAIRO_GL_SHADER_IN_COUNT -} cairo_gl_shader_in_t; - -typedef enum cairo_gl_var_type { - CAIRO_GL_VAR_NONE, - CAIRO_GL_VAR_TEXCOORDS, - CAIRO_GL_VAR_TEXGEN, -} cairo_gl_var_type_t; - -typedef enum cairo_gl_primitive_type { - CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES, - CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS -} cairo_gl_primitive_type_t; - -typedef void (*cairo_gl_emit_rect_t) (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2); - -typedef void (*cairo_gl_emit_span_t) (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - uint8_t alpha); - -typedef void (*cairo_gl_emit_glyph_t) (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2, - GLfloat glyph_x1, GLfloat glyph_y1, - GLfloat glyph_x2, GLfloat glyph_y2); - -#define cairo_gl_var_type_hash(src,mask,spans,dest) ((spans) << 5) | ((mask) << 3 | (src << 1) | (dest)) -#define CAIRO_GL_VAR_TYPE_MAX (1 << 6) - -typedef void (*cairo_gl_generic_func_t)(void); -typedef cairo_gl_generic_func_t (*cairo_gl_get_proc_addr_func_t)(const char *procname); - -typedef struct _cairo_gl_dispatch { - /* Buffers */ - void (*GenBuffers) (GLsizei n, GLuint *buffers); - void (*BindBuffer) (GLenum target, GLuint buffer); - void (*BufferData) (GLenum target, GLsizeiptr size, - const GLvoid* data, GLenum usage); - GLvoid *(*MapBuffer) (GLenum target, GLenum access); - GLboolean (*UnmapBuffer) (GLenum target); - - /* Shaders */ - GLuint (*CreateShader) (GLenum type); - void (*ShaderSource) (GLuint shader, GLsizei count, - const GLchar** string, const GLint* length); - void (*CompileShader) (GLuint shader); - void (*GetShaderiv) (GLuint shader, GLenum pname, GLint *params); - void (*GetShaderInfoLog) (GLuint shader, GLsizei bufSize, - GLsizei *length, GLchar *infoLog); - void (*DeleteShader) (GLuint shader); - - /* Programs */ - GLuint (*CreateProgram) (void); - void (*AttachShader) (GLuint program, GLuint shader); - void (*DeleteProgram) (GLuint program); - void (*LinkProgram) (GLuint program); - void (*UseProgram) (GLuint program); - void (*GetProgramiv) (GLuint program, GLenum pname, GLint *params); - void (*GetProgramInfoLog) (GLuint program, GLsizei bufSize, - GLsizei *length, GLchar *infoLog); - - /* Uniforms */ - GLint (*GetUniformLocation) (GLuint program, const GLchar* name); - void (*Uniform1f) (GLint location, GLfloat x); - void (*Uniform2f) (GLint location, GLfloat x, GLfloat y); - void (*Uniform3f) (GLint location, GLfloat x, GLfloat y, GLfloat z); - void (*Uniform4f) (GLint location, GLfloat x, GLfloat y, GLfloat z, - GLfloat w); - void (*UniformMatrix3fv) (GLint location, GLsizei count, - GLboolean transpose, const GLfloat *value); - void (*UniformMatrix4fv) (GLint location, GLsizei count, - GLboolean transpose, const GLfloat *value); - void (*Uniform1i) (GLint location, GLint x); - - /* Attributes */ - void (*BindAttribLocation) (GLuint program, GLuint index, - const GLchar *name); - void (*VertexAttribPointer) (GLuint index, GLint size, GLenum type, - GLboolean normalized, GLsizei stride, - const GLvoid *pointer); - void (*EnableVertexAttribArray) (GLuint index); - void (*DisableVertexAttribArray) (GLuint index); - - /* Framebuffer objects */ - void (*GenFramebuffers) (GLsizei n, GLuint* framebuffers); - void (*BindFramebuffer) (GLenum target, GLuint framebuffer); - void (*FramebufferTexture2D) (GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, - GLint level); - GLenum (*CheckFramebufferStatus) (GLenum target); - void (*DeleteFramebuffers) (GLsizei n, const GLuint* framebuffers); - void (*GenRenderbuffers) (GLsizei n, GLuint *renderbuffers); - void (*BindRenderbuffer) (GLenum target, GLuint renderbuffer); - void (*RenderbufferStorage) (GLenum target, GLenum internal_format, - GLsizei width, GLsizei height); - void (*FramebufferRenderbuffer) (GLenum target, GLenum attachment, - GLenum renderbuffer_ttarget, GLuint renderbuffer); - void (*DeleteRenderbuffers) (GLsizei n, GLuint *renderbuffers); - void (*BlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter); - void (*RenderbufferStorageMultisample) (GLenum target, GLsizei samples, - GLenum internalformat, - GLsizei width, GLsizei height); - void (*FramebufferTexture2DMultisample) (GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, - GLint level, GLsizei samples); -} cairo_gl_dispatch_t; - -struct _cairo_gl_context { - cairo_device_t base; - - const cairo_compositor_t *compositor; - - GLuint texture_load_pbo; - GLint max_framebuffer_size; - GLint max_texture_size; - GLint max_textures; - GLenum tex_target; - - GLint num_samples; - cairo_bool_t supports_msaa; - char *vb; - - cairo_bool_t has_shader_support; - - GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX]; - cairo_gl_shader_t fill_rectangles_shader; - cairo_cache_t shaders; - - cairo_cache_t gradients; - - cairo_gl_glyph_cache_t glyph_cache[2]; - cairo_list_t fonts; - - cairo_gl_surface_t *current_target; - cairo_operator_t current_operator; - cairo_gl_shader_t *pre_shader; /* for component alpha */ - cairo_gl_shader_t *current_shader; - - cairo_gl_operand_t operands[2]; - cairo_bool_t spans; - - unsigned int vbo_size; - unsigned int vb_offset; - unsigned int vertex_size; - cairo_region_t *clip_region; - cairo_clip_t *clip; - - cairo_gl_primitive_type_t primitive_type; - cairo_array_t tristrip_indices; - - cairo_bool_t has_mesa_pack_invert; - cairo_gl_dispatch_t dispatch; - GLfloat modelviewprojection_matrix[16]; - cairo_gl_flavor_t gl_flavor; - cairo_bool_t has_map_buffer; - cairo_bool_t has_packed_depth_stencil; - cairo_bool_t has_npot_repeat; - cairo_bool_t can_read_bgra; - - cairo_bool_t thread_aware; - - void (*acquire) (void *ctx); - void (*release) (void *ctx); - - void (*make_current) (void *ctx, cairo_gl_surface_t *surface); - void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); - void (*destroy) (void *ctx); -}; - -typedef struct _cairo_gl_composite { - cairo_gl_surface_t *dst; - cairo_operator_t op; - cairo_region_t *clip_region; - - cairo_gl_operand_t src; - cairo_gl_operand_t mask; - cairo_bool_t spans; - - cairo_clip_t *clip; - cairo_bool_t multisample; -} cairo_gl_composite_t; - -typedef struct _cairo_gl_font { - cairo_scaled_font_private_t base; - cairo_device_t *device; - cairo_list_t link; -} cairo_gl_font_t; - -static cairo_always_inline GLenum -_cairo_gl_get_error (void) -{ - GLenum err = glGetError(); - - if (unlikely (err)) - while (glGetError ()); - - return err; -} - -static inline cairo_device_t * -_cairo_gl_context_create_in_error (cairo_status_t status) -{ - return (cairo_device_t *) _cairo_device_create_in_error (status); -} - -cairo_private cairo_status_t -_cairo_gl_context_init (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_surface_init (cairo_device_t *device, - cairo_gl_surface_t *surface, - cairo_content_t content, - int width, int height); - -static cairo_always_inline cairo_bool_t cairo_warn -_cairo_gl_surface_is_texture (cairo_gl_surface_t *surface) -{ - return surface->tex != 0; -} - -cairo_private cairo_status_t -_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, - cairo_image_surface_t *src, - int src_x, int src_y, - int width, int height, - int dst_x, int dst_y, - cairo_bool_t force_flush); - -cairo_private cairo_int_status_t -_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface); - -static cairo_always_inline cairo_bool_t -_cairo_gl_device_has_glsl (cairo_device_t *device) -{ - return ((cairo_gl_context_t *) device)->has_shader_support; -} - -static cairo_always_inline cairo_bool_t -_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device) -{ - return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE; -} - -static cairo_always_inline cairo_status_t cairo_warn -_cairo_gl_context_acquire (cairo_device_t *device, - cairo_gl_context_t **ctx) -{ - cairo_status_t status; - - status = cairo_device_acquire (device); - if (unlikely (status)) - return status; - - /* clear potential previous GL errors */ - _cairo_gl_get_error (); - - *ctx = (cairo_gl_context_t *) device; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_always_inline cairo_warn cairo_status_t -_cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status) -{ - GLenum err; - - err = _cairo_gl_get_error (); - - if (unlikely (err)) { - cairo_status_t new_status; - new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); - if (status == CAIRO_STATUS_SUCCESS) - status = new_status; - } - - cairo_device_release (&(ctx)->base); - - return status; -} - -cairo_private void -_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface, - cairo_bool_t multisampling); - -cairo_private void -_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface, - cairo_bool_t multisampling); - -cairo_private cairo_gl_emit_rect_t -_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx, - GLfloat x1, GLfloat y1, - GLfloat x2, GLfloat y2); - -cairo_private cairo_gl_emit_span_t -_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx); - -cairo_private cairo_gl_emit_glyph_t -_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_context_activate (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit); - -cairo_private cairo_bool_t -_cairo_gl_operator_is_supported (cairo_operator_t op); - -cairo_private cairo_bool_t -_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface); - -cairo_private cairo_status_t -_cairo_gl_composite_init (cairo_gl_composite_t *setup, - cairo_operator_t op, - cairo_gl_surface_t *dst, - cairo_bool_t has_component_alpha); - -cairo_private void -_cairo_gl_composite_fini (cairo_gl_composite_t *setup); - -cairo_private cairo_status_t -_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup, - cairo_operator_t op, - cairo_bool_t assume_component_alpha); - -cairo_private void -_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, - cairo_region_t *clip_region); - -cairo_private void -_cairo_gl_composite_set_clip(cairo_gl_composite_t *setup, - cairo_clip_t *clip); - -cairo_private cairo_int_status_t -_cairo_gl_composite_set_source (cairo_gl_composite_t *setup, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen); - -cairo_private void -_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup, - const cairo_color_t *color); - -cairo_private void -_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, - const cairo_gl_operand_t *source); - -cairo_private cairo_int_status_t -_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen); - -cairo_private void -_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup, - const cairo_gl_operand_t *mask); - -cairo_private void -_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup); - -cairo_private void -_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup); - -cairo_private cairo_status_t -_cairo_gl_composite_begin (cairo_gl_composite_t *setup, - cairo_gl_context_t **ctx); - -cairo_private cairo_status_t -_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup, - cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_composite_flush (cairo_gl_context_t *ctx); - -cairo_private cairo_int_status_t -_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - const cairo_point_t quad[4]); - -cairo_private cairo_int_status_t -_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - const cairo_point_t triangle[3]); - -cairo_private void -_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, - cairo_gl_tex_t tex_unit); - -cairo_private cairo_bool_t -_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, - pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha, - cairo_bool_t *needs_swap); - -cairo_private void -_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache); - -cairo_private void -_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, - cairo_gl_glyph_cache_t *cache); - -cairo_private cairo_int_status_t -_cairo_gl_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip, - int *remaining_glyphs); - -cairo_private cairo_status_t -_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx); - -cairo_private void -_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx); - -static cairo_always_inline cairo_bool_t -_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx) -{ - return ctx->vb_offset == 0; -} - -cairo_private cairo_status_t -_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, - cairo_gl_operand_t *source, - cairo_gl_operand_t *mask, - cairo_bool_t use_coverage, - cairo_gl_shader_in_t in, - cairo_gl_shader_t **shader); - -cairo_private void -_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, - GLint location, - float value); - -cairo_private void -_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, - GLint location, - float value0, float value1); - -cairo_private void -_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, - GLint location, - float value0, - float value1, - float value2); - -cairo_private void -_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, - GLint location, - float value0, float value1, - float value2, float value3); - -cairo_private void -_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, - GLint location, - const cairo_matrix_t* m); - -cairo_private void -_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, - GLint location, - GLfloat* gl_m); - -cairo_private void -_cairo_gl_set_shader (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader); - -cairo_private void -_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader); - -cairo_private int -_cairo_gl_get_version (void); - -cairo_private cairo_gl_flavor_t -_cairo_gl_get_flavor (void); - -cairo_private unsigned long -_cairo_gl_get_vbo_size (void); - -cairo_private cairo_bool_t -_cairo_gl_has_extension (const char *ext); - -cairo_private cairo_status_t -_cairo_gl_dispatch_init(cairo_gl_dispatch_t *dispatch, - cairo_gl_get_proc_addr_func_t get_proc_addr); - -cairo_private cairo_int_status_t -_cairo_gl_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *pattern, - cairo_gl_surface_t *dst, - const cairo_rectangle_int_t *sample, - const cairo_rectangle_int_t *extents, - cairo_bool_t use_texgen); - -cairo_private void -_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand, - const cairo_color_t *color); - -cairo_private cairo_filter_t -_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand); - -cairo_private GLint -_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand); - -cairo_private cairo_extend_t -_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand); - -cairo_private unsigned int -_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand); - -cairo_private cairo_bool_t -_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest, - cairo_gl_operand_t *source, - unsigned int vertex_offset); - -cairo_private void -_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, - cairo_gl_operand_t *operand, - cairo_gl_tex_t tex_unit); - -cairo_private void -_cairo_gl_operand_emit (cairo_gl_operand_t *operand, - GLfloat ** vb, - GLfloat x, - GLfloat y); - -cairo_private void -_cairo_gl_operand_copy (cairo_gl_operand_t *dst, - const cairo_gl_operand_t *src); - -cairo_private void -_cairo_gl_operand_translate (cairo_gl_operand_t *operand, - double tx, double ty); - -cairo_private void -_cairo_gl_operand_destroy (cairo_gl_operand_t *operand); - -cairo_private const cairo_compositor_t * -_cairo_gl_msaa_compositor_get (void); - -cairo_private const cairo_compositor_t * -_cairo_gl_span_compositor_get (void); - -cairo_private const cairo_compositor_t * -_cairo_gl_traps_compositor_get (void); - -cairo_private cairo_int_status_t -_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int *num_glyphs); - -cairo_private cairo_int_status_t -_cairo_gl_composite_glyphs (void *_dst, - cairo_operator_t op, - cairo_surface_t *_src, - int src_x, - int src_y, - int dst_x, - int dst_y, - cairo_composite_glyphs_info_t *info); - -cairo_private cairo_int_status_t -_cairo_gl_composite_glyphs_with_clip (void *_dst, - cairo_operator_t op, - cairo_surface_t *_src, - int src_x, - int src_y, - int dst_x, - int dst_y, - cairo_composite_glyphs_info_t *info, - cairo_clip_t *clip); - -cairo_private void -_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx, - cairo_gl_surface_t *surface); - -cairo_private cairo_surface_t * -_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height); - -cairo_private cairo_surface_t * -_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height); - -cairo_private cairo_surface_t * -_cairo_gl_pattern_to_source (cairo_surface_t *dst, - const cairo_pattern_t *pattern, - cairo_bool_t is_mask, - const cairo_rectangle_int_t *extents, - const cairo_rectangle_int_t *sample, - int *src_x, int *src_y); - -cairo_private cairo_int_status_t -_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx, - cairo_gl_composite_t *setup, - cairo_clip_t *clip); - -cairo_private cairo_surface_t * -_cairo_gl_white_source (void); - -cairo_private void -_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface, - const cairo_rectangle_int_t *r); - -static inline cairo_gl_operand_t * -source_to_operand (cairo_surface_t *surface) -{ - cairo_gl_source_t *source = (cairo_gl_source_t *)surface; - return source ? &source->operand : NULL; -} - -static inline void -_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_unpin (&cache->rtree); -} - - -slim_hidden_proto (cairo_gl_surface_create); -slim_hidden_proto (cairo_gl_surface_create_for_texture); - -#endif /* CAIRO_GL_PRIVATE_H */ diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c deleted file mode 100644 index b70c177f2..000000000 --- a/src/cairo-gl-shaders.c +++ /dev/null @@ -1,1111 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 T. Zachary Laine - * Copyright © 2010 Eric Anholt - * Copyright © 2010 Red Hat, Inc - * Copyright © 2010 Linaro Limited - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 T. Zachary Laine. - * - * Contributor(s): - * Benjamin Otte <otte@gnome.org> - * Eric Anholt <eric@anholt.net> - * T. Zachary Laine <whatwasthataddress@gmail.com> - * Alexandros Frantzis <alexandros.frantzis@linaro.org> - * H. Lewin <heiko.lewin@gmx.de> - */ - -#include "cairoint.h" -#include "cairo-gl-private.h" -#include "cairo-error-private.h" -#include "cairo-output-stream-private.h" - -static cairo_status_t -_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - cairo_bool_t use_coverage, - const char *fragment_text); - -typedef struct _cairo_shader_cache_entry { - cairo_cache_entry_t base; - - unsigned vertex; - - cairo_gl_operand_type_t src; - cairo_gl_operand_type_t mask; - cairo_gl_operand_type_t dest; - cairo_bool_t use_coverage; - - cairo_gl_shader_in_t in; - GLint src_gl_filter; - cairo_bool_t src_border_fade; - cairo_extend_t src_extend; - GLint mask_gl_filter; - cairo_bool_t mask_border_fade; - cairo_extend_t mask_extend; - - cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ - cairo_gl_shader_t shader; -} cairo_shader_cache_entry_t; - -static cairo_bool_t -_cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b) -{ - const cairo_shader_cache_entry_t *a = key_a; - const cairo_shader_cache_entry_t *b = key_b; - cairo_bool_t both_have_npot_repeat = - a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; - - return (a->vertex == b->vertex && - a->src == b->src && - a->mask == b->mask && - a->dest == b->dest && - a->use_coverage == b->use_coverage && - a->in == b->in && - (both_have_npot_repeat || a->src_extend == b->src_extend) && - (both_have_npot_repeat || a->mask_extend == b->mask_extend)); -} - -/* - * For GLES2 we use more complicated shaders to implement missing GL - * features. In this case we need more parameters to uniquely identify - * a shader (vs _cairo_gl_shader_cache_equal_desktop()). - */ -static cairo_bool_t -_cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b) -{ - const cairo_shader_cache_entry_t *a = key_a; - const cairo_shader_cache_entry_t *b = key_b; - cairo_bool_t both_have_npot_repeat = - a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; - - return (a->vertex == b->vertex && - a->src == b->src && - a->mask == b->mask && - a->dest == b->dest && - a->use_coverage == b->use_coverage && - a->in == b->in && - a->src_gl_filter == b->src_gl_filter && - a->src_border_fade == b->src_border_fade && - (both_have_npot_repeat || a->src_extend == b->src_extend) && - a->mask_gl_filter == b->mask_gl_filter && - a->mask_border_fade == b->mask_border_fade && - (both_have_npot_repeat || a->mask_extend == b->mask_extend)); -} - -static unsigned long -_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) -{ - return (((uint32_t)entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage) ^ entry->vertex; -} - -static void -_cairo_gl_shader_cache_destroy (void *data) -{ - cairo_shader_cache_entry_t *entry = data; - - _cairo_gl_shader_fini (entry->ctx, &entry->shader); - if (entry->ctx->current_shader == &entry->shader) - entry->ctx->current_shader = NULL; - free (entry); -} - -static void -_cairo_gl_shader_init (cairo_gl_shader_t *shader) -{ - shader->fragment_shader = 0; - shader->program = 0; -} - -cairo_status_t -_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) -{ - static const char *fill_fs_source = - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "uniform vec4 color;\n" - "void main()\n" - "{\n" - " gl_FragColor = color;\n" - "}\n"; - cairo_status_t status; - - if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) || - (_cairo_gl_has_extension ("GL_ARB_shader_objects") && - _cairo_gl_has_extension ("GL_ARB_fragment_shader") && - _cairo_gl_has_extension ("GL_ARB_vertex_shader"))) { - ctx->has_shader_support = TRUE; - } else { - ctx->has_shader_support = FALSE; - fprintf (stderr, "Error: The cairo gl backend requires shader support!\n"); - return CAIRO_STATUS_DEVICE_ERROR; - } - - memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders)); - - status = _cairo_cache_init (&ctx->shaders, - ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ? - _cairo_gl_shader_cache_equal_desktop : - _cairo_gl_shader_cache_equal_gles2, - NULL, - _cairo_gl_shader_cache_destroy, - CAIRO_GL_MAX_SHADERS_PER_CONTEXT); - if (unlikely (status)) - return status; - - _cairo_gl_shader_init (&ctx->fill_rectangles_shader); - status = _cairo_gl_shader_compile_and_link (ctx, - &ctx->fill_rectangles_shader, - CAIRO_GL_VAR_NONE, - CAIRO_GL_VAR_NONE, - FALSE, - fill_fs_source); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx) -{ - int i; - - for (i = 0; i < CAIRO_GL_VAR_TYPE_MAX; i++) { - if (ctx->vertex_shaders[i]) - ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]); - } - - _cairo_gl_shader_fini(ctx, &ctx->fill_rectangles_shader); - _cairo_cache_fini (&ctx->shaders); -} - -void -_cairo_gl_shader_fini (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader) -{ - if (shader->fragment_shader) - ctx->dispatch.DeleteShader (shader->fragment_shader); - - if (shader->program) - ctx->dispatch.DeleteProgram (shader->program); -} - -static const char *operand_names[] = { "source", "mask", "dest" }; - -static cairo_gl_var_type_t -cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand) -{ - switch (operand->type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_CONSTANT: - return CAIRO_GL_VAR_NONE; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - return operand->gradient.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS; - case CAIRO_GL_OPERAND_TEXTURE: - return operand->texture.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS; - } -} - -static void -cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, - cairo_gl_var_type_t type, - cairo_gl_tex_t name) -{ - switch (type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_GL_VAR_NONE: - break; - case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - "attribute vec4 MultiTexCoord%d;\n" - "varying vec2 %s_texcoords;\n", - name, - operand_names[name]); - break; - case CAIRO_GL_VAR_TEXGEN: - _cairo_output_stream_printf (stream, - "uniform mat3 %s_texgen;\n" - "varying vec2 %s_texcoords;\n", - operand_names[name], - operand_names[name]); - break; - } -} - -static void -cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, - cairo_gl_var_type_t type, - cairo_gl_tex_t name) -{ - switch (type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_GL_VAR_NONE: - break; - case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - " %s_texcoords = MultiTexCoord%d.xy;\n", - operand_names[name], name); - break; - - case CAIRO_GL_VAR_TEXGEN: - _cairo_output_stream_printf (stream, - " %s_texcoords = (%s_texgen * Vertex.xyw).xy;\n", - operand_names[name], operand_names[name]); - break; - } -} - -static void -cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream) -{ - _cairo_output_stream_printf (stream, "varying float coverage;\n"); -} - -static void -cairo_gl_shader_def_coverage (cairo_output_stream_t *stream) -{ - _cairo_output_stream_printf (stream, " coverage = Color.a;\n"); -} - -static cairo_status_t -cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - cairo_bool_t use_coverage, - cairo_gl_var_type_t dest, - char **out) -{ - cairo_output_stream_t *stream = _cairo_memory_stream_create (); - unsigned char *source; - unsigned long length; - cairo_status_t status; - - cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK); - if (use_coverage) - cairo_gl_shader_dcl_coverage (stream); - - _cairo_output_stream_printf (stream, - "attribute vec4 Vertex;\n" - "attribute vec4 Color;\n" - "uniform mat4 ModelViewProjectionMatrix;\n" - "void main()\n" - "{\n" - " gl_Position = ModelViewProjectionMatrix * Vertex;\n"); - - cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK); - if (use_coverage) - cairo_gl_shader_def_coverage (stream); - - _cairo_output_stream_write (stream, - "}\n\0", 3); - - status = _cairo_memory_stream_destroy (stream, &source, &length); - if (unlikely (status)) - return status; - - *out = (char *) source; - return CAIRO_STATUS_SUCCESS; -} - -/* - * Returns whether an operand needs a special border fade fragment shader - * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2. - */ -static cairo_bool_t -_cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand) -{ - cairo_extend_t extend =_cairo_gl_operand_get_extend (operand); - - return extend == CAIRO_EXTEND_NONE && - (operand->type == CAIRO_GL_OPERAND_TEXTURE || - operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT || - operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE || - operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0); -} - -static void -cairo_gl_shader_emit_color (cairo_output_stream_t *stream, - cairo_gl_context_t *ctx, - cairo_gl_operand_t *op, - cairo_gl_tex_t name) -{ - const char *namestr = operand_names[name]; - const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : ""); - - switch (op->type) { - case CAIRO_GL_OPERAND_COUNT: - default: - ASSERT_NOT_REACHED; - break; - case CAIRO_GL_OPERAND_NONE: - _cairo_output_stream_printf (stream, - "vec4 get_%s()\n" - "{\n" - " return vec4 (0, 0, 0, 1);\n" - "}\n", - namestr); - break; - case CAIRO_GL_OPERAND_CONSTANT: - _cairo_output_stream_printf (stream, - "uniform vec4 %s_constant;\n" - "vec4 get_%s()\n" - "{\n" - " return %s_constant;\n" - "}\n", - namestr, namestr, namestr); - break; - case CAIRO_GL_OPERAND_TEXTURE: - _cairo_output_stream_printf (stream, - "uniform sampler2D%s %s_sampler;\n" - "uniform vec2 %s_texdims;\n" - "varying vec2 %s_texcoords;\n" - "vec4 get_%s()\n" - "{\n", - rectstr, namestr, namestr, namestr, namestr); - if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && - _cairo_gl_shader_needs_border_fade (op)) - { - _cairo_output_stream_printf (stream, - " vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n" - " vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n" - " return texel * border_fade.x * border_fade.y;\n" - "}\n", - namestr, namestr, namestr, rectstr, namestr, namestr); - } - else - { - _cairo_output_stream_printf (stream, - " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n" - "}\n", - rectstr, namestr, namestr, namestr); - } - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n" - "uniform vec2 %s_texdims;\n" - "uniform sampler2D%s %s_sampler;\n" - "\n" - "vec4 get_%s()\n" - "{\n", - namestr, namestr, rectstr, namestr, namestr); - if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && - _cairo_gl_shader_needs_border_fade (op)) - { - _cairo_output_stream_printf (stream, - " float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n" - " vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n" - " return texel * border_fade;\n" - "}\n", - namestr, namestr, namestr, rectstr, namestr, namestr); - } - else - { - _cairo_output_stream_printf (stream, - " return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n" - "}\n", - rectstr, namestr, namestr, namestr); - } - break; - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n" - "uniform vec2 %s_texdims;\n" - "uniform sampler2D%s %s_sampler;\n" - "uniform vec3 %s_circle_d;\n" - "uniform float %s_radius_0;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" - " \n" - " float B = dot (pos, %s_circle_d);\n" - " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" - " \n" - " float t = 0.5 * C / B;\n" - " float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n", - namestr, namestr, rectstr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr); - if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && - _cairo_gl_shader_needs_border_fade (op)) - { - _cairo_output_stream_printf (stream, - " float border_fade = %s_border_fade (t, %s_texdims.x);\n" - " vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n" - " return mix (vec4 (0.0), texel * border_fade, is_valid);\n" - "}\n", - namestr, namestr, rectstr, namestr); - } - else - { - _cairo_output_stream_printf (stream, - " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n" - " return mix (vec4 (0.0), texel, is_valid);\n" - "}\n", - rectstr, namestr, namestr); - } - break; - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n" - "uniform vec2 %s_texdims;\n" - "uniform sampler2D%s %s_sampler;\n" - "uniform vec3 %s_circle_d;\n" - "uniform float %s_a;\n" - "uniform float %s_radius_0;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" - " \n" - " float B = dot (pos, %s_circle_d);\n" - " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" - " \n" - " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n" - " float sqrtdet = sqrt (abs (det));\n" - " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n" - " \n" - " vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n" - " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" - " \n" - " float upper_t = mix (t.y, t.x, is_valid.x);\n", - namestr, namestr, rectstr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr, namestr); - if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && - _cairo_gl_shader_needs_border_fade (op)) - { - _cairo_output_stream_printf (stream, - " float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n" - " vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n" - " return mix (vec4 (0.0), texel * border_fade, has_color);\n" - "}\n", - namestr, namestr, rectstr, namestr); - } - else - { - _cairo_output_stream_printf (stream, - " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" - " return mix (vec4 (0.0), texel, has_color);\n" - "}\n", - rectstr, namestr, namestr); - } - break; - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n" - "uniform sampler2D%s %s_sampler;\n" - "uniform vec3 %s_circle_d;\n" - "uniform float %s_a;\n" - "uniform float %s_radius_0;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" - " \n" - " float B = dot (pos, %s_circle_d);\n" - " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" - " \n" - " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n" - " float sqrtdet = sqrt (abs (det));\n" - " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n" - " \n" - " vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n" - " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" - " \n" - " float upper_t = mix (t.y, t.x, is_valid.x);\n" - " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" - " return mix (vec4 (0.0), texel, has_color);\n" - "}\n", - namestr, rectstr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, rectstr, namestr, namestr); - break; - } -} - -/* - * Emits the border fade functions used by an operand. - * - * If bilinear filtering is used, the emitted function performs a linear - * fade to transparency effect in the intervals [-1/2n, 1/2n] and - * [1 - 1/2n, 1 + 1/2n] (n: texture size). - * - * If nearest filtering is used, the emitted function just returns - * 0.0 for all values outside [0, 1). - */ -static void -_cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream, - cairo_gl_operand_t *operand, - cairo_gl_tex_t name) -{ - const char *namestr = operand_names[name]; - GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand); - - /* 2D version */ - _cairo_output_stream_printf (stream, - "vec2 %s_border_fade (vec2 coords, vec2 dims)\n" - "{\n", - namestr); - - if (gl_filter == GL_LINEAR) - _cairo_output_stream_printf (stream, - " return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n"); - else - _cairo_output_stream_printf (stream, - " bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n" - " bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n" - " return vec2 (float (all (in_tex1) && all (in_tex2)));\n"); - - _cairo_output_stream_printf (stream, "}\n"); - - /* 1D version */ - _cairo_output_stream_printf (stream, - "float %s_border_fade (float x, float dim)\n" - "{\n", - namestr); - if (gl_filter == GL_LINEAR) - _cairo_output_stream_printf (stream, - " return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n"); - else - _cairo_output_stream_printf (stream, - " bool in_tex = x >= 0.0 && x < 1.0;\n" - " return float (in_tex);\n"); - - _cairo_output_stream_printf (stream, "}\n"); -} - -/* - * Emits the wrap function used by an operand. - * - * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are - * only available for NPOT textures if the GL_OES_texture_npot is supported. - * If GL_OES_texture_npot is not supported, we need to implement the wrapping - * functionality in the shader. - */ -static void -_cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, - cairo_output_stream_t *stream, - cairo_gl_operand_t *operand, - cairo_gl_tex_t name) -{ - const char *namestr = operand_names[name]; - cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); - - _cairo_output_stream_printf (stream, - "vec2 %s_wrap(vec2 coords)\n" - "{\n", - namestr); - - if (! ctx->has_npot_repeat && - (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) - { - if (extend == CAIRO_EXTEND_REPEAT) { - _cairo_output_stream_printf (stream, - " return fract(coords);\n"); - } else { /* CAIRO_EXTEND_REFLECT */ - _cairo_output_stream_printf (stream, - " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); - } - } - else - { - _cairo_output_stream_printf (stream, " return coords;\n"); - } - - _cairo_output_stream_printf (stream, "}\n"); -} - -static cairo_status_t -cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, - cairo_gl_shader_in_t in, - cairo_gl_operand_t *src, - cairo_gl_operand_t *mask, - cairo_bool_t use_coverage, - cairo_gl_operand_type_t dest_type, - char **out) -{ - cairo_output_stream_t *stream = _cairo_memory_stream_create (); - unsigned char *source; - unsigned long length; - cairo_status_t status; - const char *coverage_str; - - _cairo_output_stream_printf (stream, - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n"); - - _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE); - _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK); - - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { - if (_cairo_gl_shader_needs_border_fade (src)) - _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE); - if (_cairo_gl_shader_needs_border_fade (mask)) - _cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK); - } - - cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK); - - coverage_str = ""; - if (use_coverage) { - _cairo_output_stream_printf (stream, "varying float coverage;\n"); - coverage_str = " * coverage"; - } - - _cairo_output_stream_printf (stream, - "void main()\n" - "{\n"); - switch (in) { - case CAIRO_GL_SHADER_IN_COUNT: - default: - ASSERT_NOT_REACHED; - case CAIRO_GL_SHADER_IN_NORMAL: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source() * get_mask().a%s;\n", - coverage_str); - break; - case CAIRO_GL_SHADER_IN_CA_SOURCE: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source() * get_mask()%s;\n", - coverage_str); - break; - case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source().a * get_mask()%s;\n", - coverage_str); - break; - } - - _cairo_output_stream_write (stream, - "}\n\0", 3); - - status = _cairo_memory_stream_destroy (stream, &source, &length); - if (unlikely (status)) - return status; - - *out = (char *) source; - return CAIRO_STATUS_SUCCESS; -} - -static void -compile_shader (cairo_gl_context_t *ctx, - GLuint *shader, - GLenum type, - const char *source) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - GLint success, log_size, num_chars; - char *log; - - *shader = dispatch->CreateShader (type); - dispatch->ShaderSource (*shader, 1, &source, 0); - dispatch->CompileShader (*shader); - dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success); - - if (success) - return; - - dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size); - if (log_size < 0) { - printf ("OpenGL shader compilation failed.\n"); - ASSERT_NOT_REACHED; - return; - } - - log = _cairo_malloc (log_size + 1); - dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log); - log[num_chars] = '\0'; - - printf ("OpenGL shader compilation failed. Shader:\n%s\n", source); - printf ("OpenGL compilation log:\n%s\n", log); - - free (log); - ASSERT_NOT_REACHED; -} - -static void -link_shader_program (cairo_gl_context_t *ctx, - GLuint *program, - GLuint vert, - GLuint frag) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - GLint success, log_size, num_chars; - char *log; - - *program = dispatch->CreateProgram (); - dispatch->AttachShader (*program, vert); - dispatch->AttachShader (*program, frag); - - dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX, - "Vertex"); - dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX, - "Color"); - dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX, - "MultiTexCoord0"); - dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX, - "MultiTexCoord1"); - - dispatch->LinkProgram (*program); - dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success); - if (success) - return; - - dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size); - if (log_size < 0) { - printf ("OpenGL shader link failed.\n"); - ASSERT_NOT_REACHED; - return; - } - - log = _cairo_malloc (log_size + 1); - dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log); - log[num_chars] = '\0'; - - printf ("OpenGL shader link failed:\n%s\n", log); - free (log); - ASSERT_NOT_REACHED; -} - -static GLint -_cairo_gl_get_op_uniform_location(cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_tex_t tex_unit, - const char *suffix) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - char uniform_name[100]; - const char *unit_name[2] = { "source", "mask" }; - - snprintf (uniform_name, sizeof (uniform_name), "%s_%s", - unit_name[tex_unit], suffix); - - return dispatch->GetUniformLocation (shader->program, uniform_name); -} - -static cairo_status_t -_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - cairo_bool_t use_coverage, - const char *fragment_text) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - unsigned int vertex_shader; - cairo_status_t status; - int i; - - assert (shader->program == 0); - - vertex_shader = cairo_gl_var_type_hash (src, mask, use_coverage, - CAIRO_GL_VAR_NONE); - if (ctx->vertex_shaders[vertex_shader] == 0) { - char *source; - - status = cairo_gl_shader_get_vertex_source (src, - mask, - use_coverage, - CAIRO_GL_VAR_NONE, - &source); - if (unlikely (status)) - goto FAILURE; - - compile_shader (ctx, &ctx->vertex_shaders[vertex_shader], - GL_VERTEX_SHADER, source); - free (source); - } - - compile_shader (ctx, &shader->fragment_shader, - GL_FRAGMENT_SHADER, fragment_text); - - link_shader_program (ctx, &shader->program, - ctx->vertex_shaders[vertex_shader], - shader->fragment_shader); - - shader->mvp_location = - dispatch->GetUniformLocation (shader->program, - "ModelViewProjectionMatrix"); - - for (i = 0; i < 2; i++) { - shader->constant_location[i] = - _cairo_gl_get_op_uniform_location (ctx, shader, i, "constant"); - shader->a_location[i] = - _cairo_gl_get_op_uniform_location (ctx, shader, i, "a"); - shader->circle_d_location[i] = - _cairo_gl_get_op_uniform_location (ctx, shader, i, "circle_d"); - shader->radius_0_location[i] = - _cairo_gl_get_op_uniform_location (ctx, shader, i, "radius_0"); - shader->texdims_location[i] = - _cairo_gl_get_op_uniform_location (ctx, shader, i, "texdims"); - shader->texgen_location[i] = - _cairo_gl_get_op_uniform_location (ctx, shader, i, "texgen"); - } - - return CAIRO_STATUS_SUCCESS; - - FAILURE: - _cairo_gl_shader_fini (ctx, shader); - shader->fragment_shader = 0; - shader->program = 0; - - return status; -} - -/* We always bind the source to texture unit 0 if present, and mask to - * texture unit 1 if present, so we can just initialize these once at - * compile time. - */ -static cairo_status_t -_cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - GLint location; - GLint saved_program; - - /* We have to save/restore the current program because we might be - * asked for a different program while a shader is bound. This shouldn't - * be a performance issue, since this is only called once per compile. - */ - glGetIntegerv (GL_CURRENT_PROGRAM, &saved_program); - dispatch->UseProgram (shader->program); - - location = dispatch->GetUniformLocation (shader->program, "source_sampler"); - if (location != -1) { - dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE); - } - - location = dispatch->GetUniformLocation (shader->program, "mask_sampler"); - if (location != -1) { - dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK); - } - if(_cairo_gl_get_error()) return CAIRO_STATUS_DEVICE_ERROR; - dispatch->UseProgram (saved_program); - /* Pop and ignore a possible gl-error when restoring the previous program. - * It may be that being selected in the gl-context was the last reference - * to the shader. - */ - _cairo_gl_get_error(); - return CAIRO_STATUS_SUCCESS; -} - -void -_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, - GLint location, - float value) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - assert (location != -1); - dispatch->Uniform1f (location, value); -} - -void -_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, - GLint location, - float value0, - float value1) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - assert (location != -1); - dispatch->Uniform2f (location, value0, value1); -} - -void -_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, - GLint location, - float value0, - float value1, - float value2) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - assert (location != -1); - dispatch->Uniform3f (location, value0, value1, value2); -} - -void -_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, - GLint location, - float value0, float value1, - float value2, float value3) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - assert (location != -1); - dispatch->Uniform4f (location, value0, value1, value2, value3); -} - -void -_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, - GLint location, - const cairo_matrix_t* m) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - float gl_m[9] = { - m->xx, m->yx, 0, - m->xy, m->yy, 0, - m->x0, m->y0, 1 - }; - assert (location != -1); - dispatch->UniformMatrix3fv (location, 1, GL_FALSE, gl_m); -} - -void -_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, - GLint location, GLfloat* gl_m) -{ - cairo_gl_dispatch_t *dispatch = &ctx->dispatch; - assert (location != -1); - dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m); -} - -void -_cairo_gl_set_shader (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader) -{ - if (ctx->current_shader == shader) - return; - - if (shader) - ctx->dispatch.UseProgram (shader->program); - else - ctx->dispatch.UseProgram (0); - - ctx->current_shader = shader; -} - -cairo_status_t -_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, - cairo_gl_operand_t *source, - cairo_gl_operand_t *mask, - cairo_bool_t use_coverage, - cairo_gl_shader_in_t in, - cairo_gl_shader_t **shader) -{ - cairo_shader_cache_entry_t lookup, *entry; - char *fs_source; - cairo_status_t status; - - lookup.ctx = ctx; - - lookup.vertex = cairo_gl_var_type_hash (cairo_gl_operand_get_var_type (source), - cairo_gl_operand_get_var_type (mask), - use_coverage, - CAIRO_GL_VAR_NONE); - - lookup.src = source->type; - lookup.mask = mask->type; - lookup.dest = CAIRO_GL_OPERAND_NONE; - lookup.use_coverage = use_coverage; - lookup.in = in; - lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source); - lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source); - lookup.src_extend = _cairo_gl_operand_get_extend (source); - lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask); - lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask); - lookup.mask_extend = _cairo_gl_operand_get_extend (mask); - lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); - lookup.base.size = 1; - - entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); - if (entry) { - assert (entry->shader.program); - *shader = &entry->shader; - return CAIRO_STATUS_SUCCESS; - } - - status = cairo_gl_shader_get_fragment_source (ctx, - in, - source, - mask, - use_coverage, - CAIRO_GL_OPERAND_NONE, - &fs_source); - if (unlikely (status)) - return status; - - entry = _cairo_malloc (sizeof (cairo_shader_cache_entry_t)); - if (unlikely (entry == NULL)) { - free (fs_source); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); - - entry->ctx = ctx; - _cairo_gl_shader_init (&entry->shader); - status = _cairo_gl_shader_compile_and_link (ctx, - &entry->shader, - cairo_gl_operand_get_var_type (source), - cairo_gl_operand_get_var_type (mask), - use_coverage, - fs_source); - free (fs_source); - - if (unlikely (status)) { - free (entry); - return status; - } - - status = _cairo_gl_shader_set_samplers (ctx, &entry->shader); - if (unlikely (status)) { - _cairo_gl_shader_fini (ctx, &entry->shader); - free (entry); - return status; - } - - status = _cairo_cache_insert (&ctx->shaders, &entry->base); - if (unlikely (status)) { - _cairo_gl_shader_fini (ctx, &entry->shader); - free (entry); - return status; - } - - *shader = &entry->shader; - - return CAIRO_STATUS_SUCCESS; -} diff --git a/src/cairo-gl-source.c b/src/cairo-gl-source.c deleted file mode 100644 index 7e0ee4a82..000000000 --- a/src/cairo-gl-source.c +++ /dev/null @@ -1,113 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2011 Intel 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Chris Wilson <chris@chris-wilson.co.uk> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-surface-backend-private.h" - -static cairo_status_t -_cairo_gl_source_finish (void *abstract_surface) -{ - cairo_gl_source_t *source = abstract_surface; - - _cairo_gl_operand_destroy (&source->operand); - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_gl_source_backend = { - CAIRO_SURFACE_TYPE_GL, - _cairo_gl_source_finish, - NULL, /* read-only wrapper */ -}; - -cairo_surface_t * -_cairo_gl_pattern_to_source (cairo_surface_t *dst, - const cairo_pattern_t *pattern, - cairo_bool_t is_mask, - const cairo_rectangle_int_t *extents, - const cairo_rectangle_int_t *sample, - int *src_x, int *src_y) -{ - cairo_gl_source_t *source; - cairo_int_status_t status; - - TRACE ((stderr, "%s\n", __FUNCTION__)); - if (pattern == NULL) - return _cairo_gl_white_source (); - - source = _cairo_malloc (sizeof (*source)); - if (unlikely (source == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&source->base, - &cairo_gl_source_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA, - FALSE); /* is_vector */ - - *src_x = *src_y = 0; - status = _cairo_gl_operand_init (&source->operand, pattern, - (cairo_gl_surface_t *)dst, - sample, extents, - FALSE); - if (unlikely (status)) { - cairo_surface_destroy (&source->base); - return _cairo_surface_create_in_error (status); - } - - return &source->base; -} - -cairo_surface_t * -_cairo_gl_white_source (void) -{ - cairo_gl_source_t *source; - - source = _cairo_malloc (sizeof (*source)); - if (unlikely (source == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&source->base, - &cairo_gl_source_backend, - NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA, - FALSE); /* is_vector */ - - _cairo_gl_solid_operand_init (&source->operand, CAIRO_COLOR_WHITE); - - return &source->base; -} diff --git a/src/cairo-gl-spans-compositor.c b/src/cairo-gl-spans-compositor.c deleted file mode 100644 index 0a4538a04..000000000 --- a/src/cairo-gl-spans-compositor.c +++ /dev/null @@ -1,556 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 Red Hat, Inc - * Copyright © 2011 Intel 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-compositor-private.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" -#include "cairo-image-surface-private.h" -#include "cairo-spans-compositor-private.h" -#include "cairo-surface-backend-private.h" - -typedef struct _cairo_gl_span_renderer { - cairo_span_renderer_t base; - - cairo_gl_composite_t setup; - double opacity; - - cairo_gl_emit_span_t emit; - - int xmin, xmax; - int ymin, ymax; - - cairo_gl_context_t *ctx; -} cairo_gl_span_renderer_t; - -static cairo_status_t -_cairo_gl_bounded_opaque_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_span_renderer_t *r = abstract_renderer; - cairo_gl_emit_span_t emit = r->emit; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - do { - if (spans[0].coverage) { - emit (r->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - } - - spans++; - } while (--num_spans > 1); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_bounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_span_renderer_t *r = abstract_renderer; - cairo_gl_emit_span_t emit = r->emit; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - do { - if (spans[0].coverage) { - emit (r->ctx, - spans[0].x, y, - spans[1].x, y + height, - r->opacity * spans[0].coverage); - } - - spans++; - } while (--num_spans > 1); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_unbounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_span_renderer_t *r = abstract_renderer; - cairo_gl_emit_span_t emit = r->emit; - - if (y > r->ymin) { - emit (r->ctx, - r->xmin, r->ymin, - r->xmax, y, - 0); - } - - if (num_spans == 0) { - emit (r->ctx, - r->xmin, y, - r->xmax, y + height, - 0); - } else { - if (spans[0].x != r->xmin) { - emit (r->ctx, - r->xmin, y, - spans[0].x, y + height, - 0); - } - - do { - emit (r->ctx, - spans[0].x, y, - spans[1].x, y + height, - r->opacity * spans[0].coverage); - spans++; - } while (--num_spans > 1); - - if (spans[0].x != r->xmax) { - emit (r->ctx, - spans[0].x, y, - r->xmax, y + height, - 0); - } - } - - r->ymin = y + height; - return CAIRO_STATUS_SUCCESS; -} - -/* XXX */ -static cairo_status_t -_cairo_gl_clipped_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_span_renderer_t *r = abstract_renderer; - cairo_gl_emit_span_t emit = r->emit; - - if (y > r->ymin) { - emit (r->ctx, - r->xmin, r->ymin, - r->xmax, y, - 0); - } - - if (num_spans == 0) { - emit (r->ctx, - r->xmin, y, - r->xmax, y + height, - 0); - } else { - if (spans[0].x != r->xmin) { - emit (r->ctx, - r->xmin, y, - spans[0].x, y + height, - 0); - } - - do { - emit (r->ctx, - spans[0].x, y, - spans[1].x, y + height, - r->opacity * spans[0].coverage); - spans++; - } while (--num_spans > 1); - - if (spans[0].x != r->xmax) { - emit (r->ctx, - spans[0].x, y, - r->xmax, y + height, - 0); - } - } - - r->ymin = y + height; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_finish_unbounded_spans (void *abstract_renderer) -{ - cairo_gl_span_renderer_t *r = abstract_renderer; - cairo_gl_emit_span_t emit = r->emit; - - if (r->ymax > r->ymin) { - emit (r->ctx, - r->xmin, r->ymin, - r->xmax, r->ymax, - 0); - } - - return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS); -} - -static cairo_status_t -_cairo_gl_finish_bounded_spans (void *abstract_renderer) -{ - cairo_gl_span_renderer_t *r = abstract_renderer; - - return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS); -} - -static void -emit_aligned_boxes (cairo_gl_context_t *ctx, - const cairo_boxes_t *boxes) -{ - const struct _cairo_boxes_chunk *chunk; - cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx); - int i; - - TRACE ((stderr, "%s: num_boxes=%d\n", __FUNCTION__, boxes->num_boxes)); - for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); - int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); - int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); - int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); - emit (ctx, x1, y1, x2, y2); - } - } -} - -static cairo_int_status_t -fill_boxes (void *_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_boxes_t *boxes) -{ - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - TRACE ((stderr, "%s\n", __FUNCTION__)); - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_solid_source (&setup, color); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - emit_aligned_boxes (ctx, boxes); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_int_status_t -draw_image_boxes (void *_dst, - cairo_image_surface_t *image, - cairo_boxes_t *boxes, - int dx, int dy) -{ - cairo_gl_surface_t *dst = _dst; - struct _cairo_boxes_chunk *chunk; - int i; - - for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - cairo_box_t *b = &chunk->base[i]; - int x = _cairo_fixed_integer_part (b->p1.x); - int y = _cairo_fixed_integer_part (b->p1.y); - int w = _cairo_fixed_integer_part (b->p2.x) - x; - int h = _cairo_fixed_integer_part (b->p2.y) - y; - cairo_status_t status; - - status = _cairo_gl_surface_draw_image (dst, image, - x + dx, y + dy, - w, h, - x, y, TRUE); - if (unlikely (status)) - return status; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t copy_boxes (void *_dst, - cairo_surface_t *_src, - cairo_boxes_t *boxes, - const cairo_rectangle_int_t *extents, - int dx, int dy) -{ - cairo_gl_surface_t *dst = _dst; - cairo_gl_surface_t *src = (cairo_gl_surface_t *)_src; - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - TRACE ((stderr, "%s\n", __FUNCTION__)); - if (! _cairo_gl_surface_is_texture (src)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (src->base.device != dst->base.device) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_source_operand (&setup, &src->operand); - _cairo_gl_operand_translate (&setup.src, -dx, -dy); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - emit_aligned_boxes (ctx, boxes); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_int_status_t -composite_boxes (void *_dst, - cairo_operator_t op, - cairo_surface_t *abstract_src, - cairo_surface_t *abstract_mask, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - cairo_boxes_t *boxes, - const cairo_rectangle_int_t *extents) -{ - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - cairo_gl_operand_t tmp_operand; - cairo_gl_operand_t *src_operand; - - TRACE ((stderr, "%s mask=(%d,%d), dst=(%d, %d)\n", __FUNCTION__, - mask_x, mask_y, dst_x, dst_y)); - - if (abstract_mask) { - if (op == CAIRO_OPERATOR_CLEAR) { - _cairo_gl_solid_operand_init (&tmp_operand, CAIRO_COLOR_WHITE); - src_operand = &tmp_operand; - op = CAIRO_OPERATOR_DEST_OUT; - } else if (op == CAIRO_OPERATOR_SOURCE) { - /* requires a LERP in the shader between dest and source */ - return CAIRO_INT_STATUS_UNSUPPORTED; - } else - src_operand = source_to_operand (abstract_src); - } else - src_operand = source_to_operand (abstract_src); - - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_source_operand (&setup, - src_operand); - _cairo_gl_operand_translate (&setup.src, -src_x, -src_y); - - _cairo_gl_composite_set_mask_operand (&setup, - source_to_operand (abstract_mask)); - _cairo_gl_operand_translate (&setup.mask, -mask_x, -mask_y); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - emit_aligned_boxes (ctx, boxes); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - if (src_operand == &tmp_operand) - _cairo_gl_operand_destroy (&tmp_operand); - return status; -} - -static cairo_int_status_t -_cairo_gl_span_renderer_init (cairo_abstract_span_renderer_t *_r, - const cairo_composite_rectangles_t *composite, - cairo_antialias_t antialias, - cairo_bool_t needs_clip) -{ - cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *)_r; - const cairo_pattern_t *source = &composite->source_pattern.base; - cairo_operator_t op = composite->op; - cairo_int_status_t status; - - if (op == CAIRO_OPERATOR_SOURCE) { - if (! _cairo_pattern_is_opaque (&composite->source_pattern.base, - &composite->source_sample_area)) - return CAIRO_INT_STATUS_UNSUPPORTED; - op = CAIRO_OPERATOR_OVER; - } - - /* XXX earlier! */ - if (op == CAIRO_OPERATOR_CLEAR) { - source = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } else if (composite->surface->is_clear && - (op == CAIRO_OPERATOR_SOURCE || - op == CAIRO_OPERATOR_OVER || - op == CAIRO_OPERATOR_ADD)) { - op = CAIRO_OPERATOR_SOURCE; - } else if (op == CAIRO_OPERATOR_SOURCE) { - /* no lerp equivalent without some major PITA */ - return CAIRO_INT_STATUS_UNSUPPORTED; - } else if (! _cairo_gl_operator_is_supported (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_gl_composite_init (&r->setup, - op, (cairo_gl_surface_t *)composite->surface, - FALSE); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_set_source (&r->setup, source, - &composite->source_sample_area, - &composite->unbounded, - TRUE); - if (unlikely (status)) - goto FAIL; - - r->opacity = 1.0; - if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { - r->opacity = composite->mask_pattern.solid.color.alpha; - } else { - status = _cairo_gl_composite_set_mask (&r->setup, - &composite->mask_pattern.base, - &composite->mask_sample_area, - &composite->unbounded, - TRUE); - if (unlikely (status)) - goto FAIL; - } - - _cairo_gl_composite_set_spans (&r->setup); - - status = _cairo_gl_composite_begin (&r->setup, &r->ctx); - if (unlikely (status)) - goto FAIL; - - r->emit = _cairo_gl_context_choose_emit_span (r->ctx); - if (composite->is_bounded) { - if (r->opacity == 1.) - r->base.render_rows = _cairo_gl_bounded_opaque_spans; - else - r->base.render_rows = _cairo_gl_bounded_spans; - r->base.finish = _cairo_gl_finish_bounded_spans; - } else { - if (needs_clip) - r->base.render_rows = _cairo_gl_clipped_spans; - else - r->base.render_rows = _cairo_gl_unbounded_spans; - r->base.finish = _cairo_gl_finish_unbounded_spans; - r->xmin = composite->unbounded.x; - r->xmax = composite->unbounded.x + composite->unbounded.width; - r->ymin = composite->unbounded.y; - r->ymax = composite->unbounded.y + composite->unbounded.height; - } - - return CAIRO_STATUS_SUCCESS; - -FAIL: - return status; -} - -static void -_cairo_gl_span_renderer_fini (cairo_abstract_span_renderer_t *_r, - cairo_int_status_t status) -{ - cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *) _r; - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return; - - if (status == CAIRO_INT_STATUS_SUCCESS) - r->base.finish (r); - - _cairo_gl_composite_fini (&r->setup); -} - -const cairo_compositor_t * -_cairo_gl_span_compositor_get (void) -{ - static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; - static cairo_spans_compositor_t spans; - static cairo_compositor_t shape; - - if (_cairo_atomic_init_once_enter(&once)) { - /* The fallback to traps here is essentially just for glyphs... */ - _cairo_shape_mask_compositor_init (&shape, - _cairo_gl_traps_compositor_get()); - shape.glyphs = NULL; - - _cairo_spans_compositor_init (&spans, &shape); - spans.fill_boxes = fill_boxes; - spans.draw_image_boxes = draw_image_boxes; - spans.copy_boxes = copy_boxes; - //spans.check_composite_boxes = check_composite_boxes; - spans.pattern_to_surface = _cairo_gl_pattern_to_source; - spans.composite_boxes = composite_boxes; - //spans.check_span_renderer = check_span_renderer; - spans.renderer_init = _cairo_gl_span_renderer_init; - spans.renderer_fini = _cairo_gl_span_renderer_fini; - - _cairo_atomic_init_once_leave(&once); - } - - return &spans.base; -} diff --git a/src/cairo-gl-surface-legacy.c b/src/cairo-gl-surface-legacy.c deleted file mode 100644 index 87dca2f03..000000000 --- a/src/cairo-gl-surface-legacy.c +++ /dev/null @@ -1,602 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#include "cairoint.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" -#include "cairo-gl-private.h" -#include "cairo-image-surface-inline.h" - -cairo_status_t -_cairo_gl_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) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - status = _cairo_gl_surface_deferred_clear (surface); - if (unlikely (status)) - return status; - - *image_extra = NULL; - return _cairo_gl_surface_get_image (surface, interest_rect, image_out, - image_rect_out); -} - -void -_cairo_gl_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) -{ - cairo_status_t status; - - status = _cairo_gl_surface_draw_image (abstract_surface, image, - 0, 0, - image->width, image->height, - image_rect->x, image_rect->y, - TRUE); - /* as we created the image, its format should be directly applicable */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_surface_destroy (&image->base); -} - -cairo_status_t -_cairo_gl_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */ - if (src->device == surface->base.device && - _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) { - status = _cairo_gl_surface_deferred_clear ((cairo_gl_surface_t *)src); - if (unlikely (status)) - return status; - - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - cairo_gl_surface_t *clone; - - clone = (cairo_gl_surface_t *) - _cairo_gl_surface_create_similar (&surface->base, - src->content, - width, height); - if (clone == NULL) - return UNSUPPORTED ("create_similar failed"); - if (clone->base.status) - return clone->base.status; - - status = _cairo_gl_surface_draw_image (clone, image_src, - src_x, src_y, - width, height, - 0, 0, TRUE); - 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; - } - - return UNSUPPORTED ("unknown src surface type in clone_similar"); -} - -/* Creates a cairo-gl pattern surface for the given trapezoids */ -static cairo_status_t -_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - int width, int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_antialias_t antialias, - cairo_surface_pattern_t *pattern) -{ - pixman_format_code_t pixman_format; - pixman_image_t *image; - cairo_surface_t *surface; - int i; - - pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, - image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); - if (unlikely (image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0; i < num_traps; i++) { - pixman_trapezoid_t trap; - - trap.top = _cairo_fixed_to_16_16 (traps[i].top); - trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom); - - trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); - trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); - trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); - trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); - - trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); - trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); - trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); - trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); - - pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); - } - - surface = _cairo_image_surface_create_for_pixman_image (image, - pixman_format); - if (unlikely (surface->status)) { - pixman_image_unref (image); - return surface->status; - } - - _cairo_pattern_init_for_surface (pattern, surface); - cairo_surface_destroy (surface); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_gl_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_context_t *ctx; - cairo_status_t status; - cairo_gl_composite_t setup; - cairo_rectangle_int_t rect = { dst_x, dst_y, width, height }; - int dx, dy; - - status = _cairo_gl_surface_deferred_clear (dst); - if (unlikely (status)) - return status; - - if (op == CAIRO_OPERATOR_SOURCE && - mask == NULL && - src->type == CAIRO_PATTERN_TYPE_SURFACE && - _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) && - _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) { - cairo_image_surface_t *image = (cairo_image_surface_t *) - ((cairo_surface_pattern_t *) src)->surface; - dx += src_x; - dy += src_y; - if (dx >= 0 && - dy >= 0 && - dx + width <= (unsigned int) image->width && - dy + height <= (unsigned int) image->height) { - status = _cairo_gl_surface_draw_image (dst, image, - dx, dy, - width, height, - dst_x, dst_y, TRUE); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - } - - status = _cairo_gl_composite_init (&setup, op, dst, - mask && mask->has_component_alpha, - &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_source (&setup, src, - src_x, src_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_mask (&setup, mask, - mask_x, mask_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - if (clip_region != NULL) { - int i, num_rectangles; - - num_rectangles = cairo_region_num_rectangles (clip_region); - - for (i = 0; i < num_rectangles; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, i, &rect); - _cairo_gl_composite_emit_rect (ctx, - rect.x, rect.y, - rect.x + rect.width, rect.y + rect.height, - 0); - } - } else { - _cairo_gl_composite_emit_rect (ctx, - dst_x, dst_y, - dst_x + width, dst_y + height, - 0); - } - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - return status; -} - -cairo_int_status_t -_cairo_gl_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, int src_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_surface_pattern_t traps_pattern; - cairo_int_status_t status; - - if (! _cairo_gl_operator_is_supported (op)) - return UNSUPPORTED ("unsupported operator"); - - status = _cairo_gl_surface_deferred_clear (dst); - if (unlikely (status)) - return status; - - status = _cairo_gl_get_traps_pattern (dst, - dst_x, dst_y, width, height, - traps, num_traps, antialias, - &traps_pattern); - if (unlikely (status)) - return status; - - status = _cairo_gl_surface_composite (op, - pattern, &traps_pattern.base, dst, - src_x, src_y, - 0, 0, - dst_x, dst_y, - width, height, - clip_region); - - _cairo_pattern_fini (&traps_pattern.base); - - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - return status; -} - -cairo_int_status_t -_cairo_gl_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_solid_pattern_t solid; - cairo_gl_context_t *ctx; - cairo_status_t status; - cairo_gl_composite_t setup; - int i; - - status = _cairo_gl_surface_deferred_clear (dst); - if (unlikely (status)) - return status; - - status = _cairo_gl_composite_init (&setup, op, dst, - FALSE, - /* XXX */ NULL); - if (unlikely (status)) - goto CLEANUP; - - _cairo_pattern_init_solid (&solid, color); - status = _cairo_gl_composite_set_source (&setup, &solid.base, - 0, 0, - 0, 0, - 0, 0); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_mask (&setup, NULL, - 0, 0, - 0, 0, - 0, 0); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - for (i = 0; i < num_rects; i++) { - _cairo_gl_composite_emit_rect (ctx, - rects[i].x, - rects[i].y, - rects[i].x + rects[i].width, - rects[i].y + rects[i].height, - 0); - } - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - return status; -} - -typedef struct _cairo_gl_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_gl_composite_t setup; - - int xmin, xmax; - int ymin, ymax; - - cairo_gl_context_t *ctx; -} cairo_gl_surface_span_renderer_t; - -static cairo_status_t -_cairo_gl_render_bounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - do { - if (spans[0].coverage) { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - } - - spans++; - } while (--num_spans > 1); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_render_unbounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (y > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, y, - 0); - } - - if (num_spans == 0) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - renderer->xmax, y + height, - 0); - } else { - if (spans[0].x != renderer->xmin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - spans[0].x, y + height, - 0); - } - - do { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - spans++; - } while (--num_spans > 1); - - if (spans[0].x != renderer->xmax) { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - renderer->xmax, y + height, - 0); - } - } - - renderer->ymin = y + height; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_finish_unbounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (renderer->ymax > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, renderer->ymax, - 0); - } - - return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); -} - -static cairo_status_t -_cairo_gl_finish_bounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); -} - -static void -_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (!renderer) - return; - - _cairo_gl_composite_fini (&renderer->setup); - - free (renderer); -} - -cairo_bool_t -_cairo_gl_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias) -{ - if (! _cairo_gl_operator_is_supported (op)) - return FALSE; - - return TRUE; - - (void) pattern; - (void) abstract_dst; - (void) antialias; -} - -cairo_span_renderer_t * -_cairo_gl_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *src, - void *abstract_dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_surface_span_renderer_t *renderer; - cairo_status_t status; - const cairo_rectangle_int_t *extents; - - status = _cairo_gl_surface_deferred_clear (dst); - if (unlikely (status)) - return _cairo_span_renderer_create_in_error (status); - - renderer = calloc (1, sizeof (*renderer)); - if (unlikely (renderer == NULL)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - - renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy; - if (rects->is_bounded) { - renderer->base.render_rows = _cairo_gl_render_bounded_spans; - renderer->base.finish = _cairo_gl_finish_bounded_spans; - extents = &rects->bounded; - } else { - renderer->base.render_rows = _cairo_gl_render_unbounded_spans; - renderer->base.finish = _cairo_gl_finish_unbounded_spans; - extents = &rects->unbounded; - } - renderer->xmin = extents->x; - renderer->xmax = extents->x + extents->width; - renderer->ymin = extents->y; - renderer->ymax = extents->y + extents->height; - - status = _cairo_gl_composite_init (&renderer->setup, - op, dst, - FALSE, extents); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_set_source (&renderer->setup, src, - extents->x, extents->y, - extents->x, extents->y, - extents->width, extents->height); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_spans (&renderer->setup); - _cairo_gl_composite_set_clip_region (&renderer->setup, - _cairo_clip_get_region (rects->clip)); - - status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx); - if (unlikely (status)) - goto FAIL; - - return &renderer->base; - -FAIL: - _cairo_gl_composite_fini (&renderer->setup); - free (renderer); - return _cairo_span_renderer_create_in_error (status); -} diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c deleted file mode 100644 index e48244dd4..000000000 --- a/src/cairo-gl-surface.c +++ /dev/null @@ -1,1552 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-compositor-private.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" -#include "cairo-image-surface-inline.h" -#include "cairo-surface-backend-private.h" - -static const cairo_surface_backend_t _cairo_gl_surface_backend; - -static cairo_status_t -_cairo_gl_surface_flush (void *abstract_surface, unsigned flags); - -static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) -{ - return surface->backend == &_cairo_gl_surface_backend; -} - -static cairo_bool_t -_cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha, - cairo_bool_t *needs_swap) -{ - cairo_bool_t is_little_endian = _cairo_is_little_endian (); - - *has_alpha = TRUE; - - switch ((int) pixman_format) { - case PIXMAN_a8r8g8b8: - *internal_format = GL_BGRA; - *format = GL_BGRA; - *type = GL_UNSIGNED_BYTE; - *needs_swap = !is_little_endian; - return TRUE; - - case PIXMAN_x8r8g8b8: - *internal_format = GL_BGRA; - *format = GL_BGRA; - *type = GL_UNSIGNED_BYTE; - *has_alpha = FALSE; - *needs_swap = !is_little_endian; - return TRUE; - - case PIXMAN_a8b8g8r8: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_BYTE; - *needs_swap = !is_little_endian; - return TRUE; - - case PIXMAN_x8b8g8r8: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_BYTE; - *has_alpha = FALSE; - *needs_swap = !is_little_endian; - return TRUE; - - case PIXMAN_b8g8r8a8: - *internal_format = GL_BGRA; - *format = GL_BGRA; - *type = GL_UNSIGNED_BYTE; - *needs_swap = is_little_endian; - return TRUE; - - case PIXMAN_b8g8r8x8: - *internal_format = GL_BGRA; - *format = GL_BGRA; - *type = GL_UNSIGNED_BYTE; - *has_alpha = FALSE; - *needs_swap = is_little_endian; - return TRUE; - - case PIXMAN_r8g8b8: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_BYTE; - *needs_swap = is_little_endian; - return TRUE; - - case PIXMAN_b8g8r8: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_BYTE; - *needs_swap = !is_little_endian; - return TRUE; - - case PIXMAN_r5g6b5: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5; - *needs_swap = FALSE; - return TRUE; - - case PIXMAN_b5g6r5: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5; - *needs_swap = TRUE; - return TRUE; - - case PIXMAN_a1b5g5r5: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_5_5_5_1; - *needs_swap = TRUE; - return TRUE; - - case PIXMAN_x1b5g5r5: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_5_5_5_1; - *has_alpha = FALSE; - *needs_swap = TRUE; - return TRUE; - - case PIXMAN_a8: - *internal_format = GL_ALPHA; - *format = GL_ALPHA; - *type = GL_UNSIGNED_BYTE; - *needs_swap = FALSE; - return TRUE; - - default: - return FALSE; - } -} - -static cairo_bool_t -_cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha, - cairo_bool_t *needs_swap) -{ - *has_alpha = TRUE; - *needs_swap = FALSE; - - switch (pixman_format) { - case PIXMAN_a8r8g8b8: - *internal_format = GL_RGBA; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - return TRUE; - case PIXMAN_x8r8g8b8: - *internal_format = GL_RGB; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_a8b8g8r8: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - return TRUE; - case PIXMAN_x8b8g8r8: - *internal_format = GL_RGB; - *format = GL_RGBA; - *type = GL_UNSIGNED_INT_8_8_8_8_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_b8g8r8a8: - *internal_format = GL_RGBA; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8; - return TRUE; - case PIXMAN_b8g8r8x8: - *internal_format = GL_RGB; - *format = GL_BGRA; - *type = GL_UNSIGNED_INT_8_8_8_8; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_r8g8b8: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_BYTE; - return TRUE; - case PIXMAN_b8g8r8: - *internal_format = GL_RGB; - *format = GL_BGR; - *type = GL_UNSIGNED_BYTE; - return TRUE; - case PIXMAN_r5g6b5: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5; - return TRUE; - case PIXMAN_b5g6r5: - *internal_format = GL_RGB; - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5_REV; - return TRUE; - case PIXMAN_a1r5g5b5: - *internal_format = GL_RGBA; - *format = GL_BGRA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - return TRUE; - case PIXMAN_x1r5g5b5: - *internal_format = GL_RGB; - *format = GL_BGRA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_a1b5g5r5: - *internal_format = GL_RGBA; - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - return TRUE; - case PIXMAN_x1b5g5r5: - *internal_format = GL_RGB; - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - *has_alpha = FALSE; - return TRUE; - case PIXMAN_a8: - *internal_format = GL_ALPHA; - *format = GL_ALPHA; - *type = GL_UNSIGNED_BYTE; - return TRUE; - -#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,27,2) - case PIXMAN_a8r8g8b8_sRGB: -#endif - case PIXMAN_a2b10g10r10: - case PIXMAN_x2b10g10r10: - case PIXMAN_a4r4g4b4: - case PIXMAN_x4r4g4b4: - case PIXMAN_a4b4g4r4: - case PIXMAN_x4b4g4r4: - case PIXMAN_r3g3b2: - case PIXMAN_b2g3r3: - case PIXMAN_a2r2g2b2: - case PIXMAN_a2b2g2r2: - case PIXMAN_c8: - case PIXMAN_x4a4: - /* case PIXMAN_x4c4: */ - case PIXMAN_x4g4: - case PIXMAN_a4: - case PIXMAN_r1g2b1: - case PIXMAN_b1g2r1: - case PIXMAN_a1r1g1b1: - case PIXMAN_a1b1g1r1: - case PIXMAN_c4: - case PIXMAN_g4: - case PIXMAN_a1: - case PIXMAN_g1: - case PIXMAN_yuy2: - case PIXMAN_yv12: - case PIXMAN_x2r10g10b10: - case PIXMAN_a2r10g10b10: - case PIXMAN_r8g8b8x8: - case PIXMAN_r8g8b8a8: - case PIXMAN_x14r6g6b6: - case PIXMAN_rgb_float: - case PIXMAN_rgba_float: - default: - return FALSE; - } -} - -/* - * Extracts pixel data from an image surface. - */ -static cairo_status_t -_cairo_gl_surface_extract_image_data (cairo_image_surface_t *image, - int x, int y, - int width, int height, - void **output) -{ - int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8; - char *data = _cairo_malloc_ab (width * height, cpp); - char *dst = data; - unsigned char *src = image->data + y * image->stride + x * cpp; - int i; - - if (unlikely (data == NULL)) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < height; i++) { - memcpy (dst, src, width * cpp); - src += image->stride; - dst += width * cpp; - } - - *output = data; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_bool_t -_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, - pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha, - cairo_bool_t *needs_swap) -{ - if (flavor == CAIRO_GL_FLAVOR_DESKTOP) - return _cairo_gl_get_image_format_and_type_gl (pixman_format, - internal_format, format, - type, has_alpha, - needs_swap); - else - return _cairo_gl_get_image_format_and_type_gles2 (pixman_format, - internal_format, format, - type, has_alpha, - needs_swap); - -} - -cairo_bool_t -_cairo_gl_operator_is_supported (cairo_operator_t op) -{ - return op < CAIRO_OPERATOR_SATURATE; -} - -static void -_cairo_gl_surface_embedded_operand_init (cairo_gl_surface_t *surface) -{ - cairo_gl_operand_t *operand = &surface->operand; - cairo_surface_attributes_t *attributes = &operand->texture.attributes; - - memset (operand, 0, sizeof (cairo_gl_operand_t)); - - operand->type = CAIRO_GL_OPERAND_TEXTURE; - operand->texture.surface = surface; - operand->texture.tex = surface->tex; - - if (_cairo_gl_device_requires_power_of_two_textures (surface->base.device)) { - cairo_matrix_init_identity (&attributes->matrix); - } else { - cairo_matrix_init_scale (&attributes->matrix, - 1.0 / surface->width, - 1.0 / surface->height); - } - - attributes->extend = CAIRO_EXTEND_NONE; - attributes->filter = CAIRO_FILTER_NEAREST; -} - -void -_cairo_gl_surface_init (cairo_device_t *device, - cairo_gl_surface_t *surface, - cairo_content_t content, - int width, int height) -{ - assert (width > 0 && height > 0); - - _cairo_surface_init (&surface->base, - &_cairo_gl_surface_backend, - device, - content, - FALSE); /* is_vector */ - - surface->width = width; - surface->height = height; - surface->needs_update = FALSE; - surface->content_in_texture = FALSE; - - _cairo_gl_surface_embedded_operand_init (surface); -} - -static cairo_bool_t -_cairo_gl_surface_size_valid_for_context (cairo_gl_context_t *ctx, - int width, int height) -{ - return width > 0 && height > 0 && - width <= ctx->max_framebuffer_size && - height <= ctx->max_framebuffer_size; -} - -static cairo_bool_t -_cairo_gl_surface_size_valid (cairo_gl_surface_t *surface, - int width, int height) -{ - cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; - return _cairo_gl_surface_size_valid_for_context (ctx, width, height); -} - -static cairo_surface_t * -_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, - cairo_content_t content, - GLuint tex, - int width, - int height) -{ - cairo_gl_surface_t *surface; - - surface = calloc (1, sizeof (cairo_gl_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface->tex = tex; - _cairo_gl_surface_init (&ctx->base, surface, content, width, height); - - surface->supports_msaa = ctx->supports_msaa; - surface->num_samples = ctx->num_samples; - surface->supports_stencil = TRUE; - - /* Create the texture used to store the surface's data. */ - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); - glBindTexture (ctx->tex_target, surface->tex); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - return &surface->base; -} - -static cairo_surface_t * -_create_scratch_internal (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height, - cairo_bool_t for_caching) -{ - cairo_gl_surface_t *surface; - GLenum format; - GLuint tex; - - glGenTextures (1, &tex); - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch_for_texture (ctx, content, - tex, width, height); - if (unlikely (surface->base.status)) - return &surface->base; - - surface->owns_tex = TRUE; - - /* adjust the texture size after setting our real extents */ - if (width < 1) - width = 1; - if (height < 1) - height = 1; - - switch (content) { - default: - ASSERT_NOT_REACHED; - case CAIRO_CONTENT_COLOR_ALPHA: - format = GL_RGBA; - break; - case CAIRO_CONTENT_ALPHA: - /* When using GL_ALPHA, compositing doesn't work properly, but for - * caching surfaces, we are just uploading pixel data, so it isn't - * an issue. */ - if (for_caching) - format = GL_ALPHA; - else - format = GL_RGBA; - break; - case CAIRO_CONTENT_COLOR: - /* GL_RGB is almost what we want here -- sampling 1 alpha when - * texturing, using 1 as destination alpha factor in blending, - * etc. However, when filtering with GL_CLAMP_TO_BORDER, the - * alpha channel of the border color will also be clamped to - * 1, when we actually want the border color we explicitly - * specified. So, we have to store RGBA, and fill the alpha - * channel with 1 when blending. - */ - format = GL_RGBA; - break; - } - - glTexImage2D (ctx->tex_target, 0, format, width, height, 0, - format, GL_UNSIGNED_BYTE, NULL); - - return &surface->base; -} - -cairo_surface_t * -_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height) -{ - return _create_scratch_internal (ctx, content, width, height, FALSE); -} - -cairo_surface_t * -_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height) -{ - return _create_scratch_internal (ctx, content, width, height, TRUE); -} - -static cairo_status_t -_cairo_gl_surface_clear (cairo_gl_surface_t *surface, - const cairo_color_t *color) -{ - cairo_gl_context_t *ctx; - cairo_status_t status; - double r, g, b, a; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - _cairo_gl_context_set_destination (ctx, surface, surface->msaa_active); - if (surface->base.content & CAIRO_CONTENT_COLOR) { - r = color->red * color->alpha; - g = color->green * color->alpha; - b = color->blue * color->alpha; - } else { - r = g = b = 0; - } - if (surface->base.content & CAIRO_CONTENT_ALPHA) { - a = color->alpha; - } else { - a = 1.0; - } - - glDisable (GL_SCISSOR_TEST); - glClearColor (r, g, b, a); - glClear (GL_COLOR_BUFFER_BIT); - - if (a == 0) - surface->base.is_clear = TRUE; - - return _cairo_gl_context_release (ctx, status); -} - -static cairo_surface_t * -_cairo_gl_surface_create_and_clear_scratch (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height) -{ - cairo_gl_surface_t *surface; - cairo_int_status_t status; - - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch (ctx, content, width, height); - if (unlikely (surface->base.status)) - return &surface->base; - - /* Cairo surfaces start out initialized to transparent (black) */ - status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); - if (unlikely (status)) { - cairo_surface_destroy (&surface->base); - return _cairo_surface_create_in_error (status); - } - - return &surface->base; -} - -cairo_surface_t * -cairo_gl_surface_create (cairo_device_t *abstract_device, - cairo_content_t content, - int width, - int height) -{ - cairo_gl_context_t *ctx; - cairo_gl_surface_t *surface; - cairo_status_t status; - - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - if (abstract_device == NULL) - return _cairo_image_surface_create_with_content (content, width, height); - - if (abstract_device->status) - return _cairo_surface_create_in_error (abstract_device->status); - - if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - status = _cairo_gl_context_acquire (abstract_device, &ctx); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) { - status = _cairo_gl_context_release (ctx, status); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - } - - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); - if (unlikely (surface->base.status)) { - status = _cairo_gl_context_release (ctx, surface->base.status); - cairo_surface_destroy (&surface->base); - return _cairo_surface_create_in_error (status); - } - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (&surface->base); - return _cairo_surface_create_in_error (status); - } - - return &surface->base; -} -slim_hidden_def (cairo_gl_surface_create); - -/** - * cairo_gl_surface_create_for_texture: - * @content: type of content in the surface - * @tex: name of texture to use for storage of surface pixels - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a GL surface for the specified texture with the specified - * content and dimensions. The texture must be kept around until the - * #cairo_surface_t is destroyed or cairo_surface_finish() is called - * on the surface. The initial contents of @tex will be used as the - * initial image contents; you must explicitly clear the buffer, - * using, for example, cairo_rectangle() and cairo_fill() if you want - * it cleared. The format of @tex should be compatible with @content, - * in the sense that it must have the color components required by - * @content. - * - * Return value: a pointer to the newly created surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a "nil" surface if an error such as out of memory - * occurs. You can use cairo_surface_status() to check for this. - * - * Since: TBD - **/ -cairo_surface_t * -cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, - cairo_content_t content, - unsigned int tex, - int width, - int height) -{ - cairo_gl_context_t *ctx; - cairo_gl_surface_t *surface; - cairo_status_t status; - - if (! CAIRO_CONTENT_VALID (content)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - - if (abstract_device == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - - if (abstract_device->status) - return _cairo_surface_create_in_error (abstract_device->status); - - if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH)); - - status = _cairo_gl_context_acquire (abstract_device, &ctx); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) { - status = _cairo_gl_context_release (ctx, status); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - } - - surface = (cairo_gl_surface_t *) - _cairo_gl_surface_create_scratch_for_texture (ctx, content, - tex, width, height); - status = _cairo_gl_context_release (ctx, status); - - return &surface->base; -} -slim_hidden_def (cairo_gl_surface_create_for_texture); - - -void -cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, - int width, - int height) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (! _cairo_surface_is_gl (abstract_surface) || - _cairo_gl_surface_is_texture (surface)) { - _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - return; - } - - if (surface->width != width || surface->height != height) { - surface->needs_update = TRUE; - surface->width = width; - surface->height = height; - } -} - -int -cairo_gl_surface_get_width (cairo_surface_t *abstract_surface) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - - if (! _cairo_surface_is_gl (abstract_surface)) - return 0; - - return surface->width; -} - -int -cairo_gl_surface_get_height (cairo_surface_t *abstract_surface) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - - if (! _cairo_surface_is_gl (abstract_surface)) - return 0; - - return surface->height; -} - -void -cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) -{ - cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - - if (unlikely (abstract_surface->status)) - return; - if (unlikely (abstract_surface->finished)) { - _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; - } - - if (! _cairo_surface_is_gl (abstract_surface)) { - _cairo_surface_set_error (abstract_surface, - CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return; - } - - if (! _cairo_gl_surface_is_texture (surface)) { - cairo_gl_context_t *ctx; - cairo_status_t status; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return; - - /* For swapping on EGL, at least, we need a valid context/target. */ - _cairo_gl_context_set_destination (ctx, surface, FALSE); - /* And in any case we should flush any pending operations. */ - _cairo_gl_composite_flush (ctx); - - ctx->swap_buffers (ctx, surface); - - status = _cairo_gl_context_release (ctx, status); - if (status) - status = _cairo_surface_set_error (abstract_surface, status); - } -} - -static cairo_surface_t * -_cairo_gl_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_surface_t *surface = abstract_surface; - cairo_gl_context_t *ctx; - cairo_status_t status; - - if (! _cairo_gl_surface_size_valid (abstract_surface, width, height)) - return _cairo_image_surface_create_with_content (content, width, height); - - status = _cairo_gl_context_acquire (surface->device, &ctx); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (surface); - return _cairo_surface_create_in_error (status); - } - - return surface; -} - -static cairo_int_status_t -_cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst, - cairo_gl_context_t *ctx, - int x, int y, - int width, int height) -{ - cairo_gl_composite_t setup; - cairo_status_t status; - - _cairo_gl_composite_flush (ctx); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - - status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, - dst, FALSE); - if (unlikely (status)) - goto CLEANUP; - - _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - _cairo_gl_context_emit_rect (ctx, x, y, x + width, y + height); - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - _cairo_gl_composite_flush (ctx); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - return status; -} - -cairo_status_t -_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, - cairo_image_surface_t *src, - int src_x, int src_y, - int width, int height, - int dst_x, int dst_y, - cairo_bool_t force_flush) -{ - GLenum internal_format, format, type; - cairo_bool_t has_alpha, needs_swap; - cairo_image_surface_t *clone = NULL; - cairo_gl_context_t *ctx; - int cpp; - cairo_image_surface_t *rgba_clone = NULL; - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; - - status = _cairo_gl_context_acquire (dst->base.device, &ctx); - if (unlikely (status)) - return status; - - if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES3 || - _cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) { - pixman_format_code_t pixman_format; - cairo_surface_pattern_t pattern; - cairo_bool_t require_conversion = FALSE; - pixman_format = _cairo_is_little_endian () ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8; - - if (src->base.content != CAIRO_CONTENT_ALPHA) { - if (src->pixman_format != pixman_format) - require_conversion = TRUE; - } - else if (dst->base.content != CAIRO_CONTENT_ALPHA) { - require_conversion = TRUE; - } - else if (src->pixman_format != PIXMAN_a8) { - pixman_format = PIXMAN_a8; - require_conversion = TRUE; - } - - if (require_conversion) { - rgba_clone = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - pixman_format, - src->width, - src->height, - 0); - if (unlikely (rgba_clone->base.status)) - goto FAIL; - - _cairo_pattern_init_for_surface (&pattern, &src->base); - status = _cairo_surface_paint (&rgba_clone->base, - CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) - goto FAIL; - - src = rgba_clone; - } - } - - if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor, - src->pixman_format, - &internal_format, - &format, - &type, - &has_alpha, - &needs_swap)) - { - cairo_bool_t is_supported; - - clone = _cairo_image_surface_coerce (src); - if (unlikely (status = clone->base.status)) - goto FAIL; - - is_supported = - _cairo_gl_get_image_format_and_type (ctx->gl_flavor, - clone->pixman_format, - &internal_format, - &format, - &type, - &has_alpha, - &needs_swap); - assert (is_supported); - assert (!needs_swap); - src = clone; - } - - cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; - - if (force_flush) { - status = _cairo_gl_surface_flush (&dst->base, 0); - if (unlikely (status)) - goto FAIL; - } - - if (_cairo_gl_surface_is_texture (dst)) { - void *data_start = src->data + src_y * src->stride + src_x * cpp; - void *data_start_gles2 = NULL; - - /* - * Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the - * image data ourselves in some cases. In particular, we must extract - * the pixels if: - * a. we don't want full-length lines or - * b. the row stride cannot be handled by GL itself using a 4 byte - * alignment constraint - */ - if (src->stride < 0 || - (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && - (src->width * cpp < src->stride - 3 || - width != src->width))) - { - glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - status = _cairo_gl_surface_extract_image_data (src, src_x, src_y, - width, height, - &data_start_gles2); - if (unlikely (status)) - goto FAIL; - - data_start = data_start_gles2; - } - else - { - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); - } - - /* we must resolve the renderbuffer to texture before we - upload image */ - status = _cairo_gl_surface_resolve_multisampling (dst); - if (unlikely (status)) { - free (data_start_gles2); - goto FAIL; - } - - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); - glBindTexture (ctx->tex_target, dst->tex); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexSubImage2D (ctx->tex_target, 0, - dst_x, dst_y, width, height, - format, type, data_start); - - free (data_start_gles2); - - /* If we just treated some rgb-only data as rgba, then we have to - * go back and fix up the alpha channel where we filled in this - * texture data. - */ - if (!has_alpha) { - _cairo_gl_surface_fill_alpha_channel (dst, ctx, - dst_x, dst_y, - width, height); - } - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - dst->content_in_texture = TRUE; - } else { - cairo_surface_t *tmp; - - tmp = _cairo_gl_surface_create_scratch (ctx, - dst->base.content, - width, height); - if (unlikely (tmp->status)) - goto FAIL; - - status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp, - src, - src_x, src_y, - width, height, - 0, 0, force_flush); - if (status == CAIRO_INT_STATUS_SUCCESS) { - cairo_surface_pattern_t tmp_pattern; - cairo_rectangle_int_t r; - cairo_clip_t *clip; - - _cairo_pattern_init_for_surface (&tmp_pattern, tmp); - cairo_matrix_init_translate (&tmp_pattern.base.matrix, - -dst_x, -dst_y); - tmp_pattern.base.filter = CAIRO_FILTER_NEAREST; - tmp_pattern.base.extend = CAIRO_EXTEND_NONE; - - r.x = dst_x; - r.y = dst_y; - r.width = width; - r.height = height; - clip = _cairo_clip_intersect_rectangle (NULL, &r); - status = _cairo_surface_paint (&dst->base, - CAIRO_OPERATOR_SOURCE, - &tmp_pattern.base, - clip); - _cairo_clip_destroy (clip); - _cairo_pattern_fini (&tmp_pattern.base); - } - - cairo_surface_destroy (tmp); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - dst->content_in_texture = TRUE; - } - -FAIL: - status = _cairo_gl_context_release (ctx, status); - - if (clone) - cairo_surface_destroy (&clone->base); - - if (rgba_clone) - cairo_surface_destroy (&rgba_clone->base); - - return status; -} - -static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface) -{ - cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; - return ctx->gl_flavor; -} - -static cairo_status_t -_cairo_gl_surface_finish (void *abstract_surface) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_gl_context_t *ctx; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) - _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); - if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) - _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); - if (ctx->current_target == surface) - ctx->current_target = NULL; - - if (surface->fb) - ctx->dispatch.DeleteFramebuffers (1, &surface->fb); - if (surface->depth_stencil) - ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil); - if (surface->owns_tex) - glDeleteTextures (1, &surface->tex); - - if (surface->msaa_depth_stencil) - ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil); - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE - if (surface->msaa_fb) - ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb); - if (surface->msaa_rb) - ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb); -#endif - - _cairo_clip_destroy (surface->clip_on_stencil_buffer); - - return _cairo_gl_context_release (ctx, status); -} - -static cairo_image_surface_t * -_cairo_gl_surface_map_to_image (void *abstract_surface, - const cairo_rectangle_int_t *extents) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_gl_context_t *ctx; - GLenum format, type; - pixman_format_code_t pixman_format; - unsigned int cpp; - cairo_bool_t flipped, mesa_invert; - cairo_status_t status; - int y; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) { - return _cairo_image_surface_create_in_error (status); - } - - /* Want to use a switch statement here but the compiler gets whiny. */ - if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { - format = GL_BGRA; - pixman_format = PIXMAN_a8r8g8b8; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - cpp = 4; - } else if (surface->base.content == CAIRO_CONTENT_COLOR) { - format = GL_BGRA; - pixman_format = PIXMAN_x8r8g8b8; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - cpp = 4; - } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { - format = GL_ALPHA; - pixman_format = PIXMAN_a8; - type = GL_UNSIGNED_BYTE; - cpp = 1; - } else { - ASSERT_NOT_REACHED; - return NULL; - } - - if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES3 || - _cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES2) { - /* If only RGBA is supported, we must download data in a compatible - * format. This means that pixman will convert the data on the CPU when - * interacting with other image surfaces. For ALPHA, GLES2 does not - * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the - * pixman image that is created has row_stride = row_width * bpp. */ - if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) { - cairo_bool_t little_endian = _cairo_is_little_endian (); - format = GL_RGBA; - - if (surface->base.content == CAIRO_CONTENT_COLOR) { - pixman_format = little_endian ? - PIXMAN_x8b8g8r8 : PIXMAN_r8g8b8x8; - } else { - pixman_format = little_endian ? - PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8; - } - } - - /* GLES2 only supports GL_UNSIGNED_BYTE. */ - type = GL_UNSIGNED_BYTE; - cpp = 4; - } - - image = (cairo_image_surface_t*) - _cairo_image_surface_create_with_pixman_format (NULL, - pixman_format, - extents->width, - extents->height, - -1); - if (unlikely (image->base.status)) { - status = _cairo_gl_context_release (ctx, status); - return image; - } - - cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y); - - /* If the original surface has not been modified or - * is clear, we can avoid downloading data. */ - if (surface->base.is_clear || surface->base.serial == 0) { - status = _cairo_gl_context_release (ctx, status); - return image; - } - - /* This is inefficient, as we'd rather just read the thing without making - * it the destination. But then, this is the fallback path, so let's not - * fall back instead. - */ - _cairo_gl_composite_flush (ctx); - - if (ctx->gl_flavor != CAIRO_GL_FLAVOR_ES3) { - _cairo_gl_context_set_destination (ctx, surface, FALSE); - } else { - if (surface->content_in_texture) { - _cairo_gl_ensure_framebuffer (ctx, surface); - ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); - } else { - status = _cairo_gl_surface_resolve_multisampling (surface); - if (unlikely (status)) { - status = _cairo_gl_context_release (ctx, status); - cairo_surface_destroy (&image->base); - return _cairo_image_surface_create_in_error (status); - } - } - } - - flipped = ! _cairo_gl_surface_is_texture (surface); - mesa_invert = flipped && ctx->has_mesa_pack_invert; - - glPixelStorei (GL_PACK_ALIGNMENT, 4); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || - ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); - if (mesa_invert) - glPixelStorei (GL_PACK_INVERT_MESA, 1); - - y = extents->y; - if (flipped) - y = surface->height - extents->y - extents->height; - - glReadPixels (extents->x, y, - extents->width, extents->height, - format, type, image->data); - if (mesa_invert) - glPixelStorei (GL_PACK_INVERT_MESA, 0); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (&image->base); - return _cairo_image_surface_create_in_error (status); - } - - /* We must invert the image manually if we lack GL_MESA_pack_invert */ - if (flipped && ! mesa_invert) { - uint8_t stack[1024], *row = stack; - uint8_t *top = image->data; - uint8_t *bot = image->data + (image->height-1)*image->stride; - - if (image->stride > (int)sizeof(stack)) { - row = _cairo_malloc (image->stride); - if (unlikely (row == NULL)) { - cairo_surface_destroy (&image->base); - return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - while (top < bot) { - memcpy (row, top, image->stride); - memcpy (top, bot, image->stride); - memcpy (bot, row, image->stride); - top += image->stride; - bot -= image->stride; - } - - if (row != stack) - free(row); - } - - image->base.is_clear = FALSE; - return image; -} - -static cairo_surface_t * -_cairo_gl_surface_source (void *abstract_surface, - cairo_rectangle_int_t *extents) -{ - cairo_gl_surface_t *surface = abstract_surface; - - if (extents) { - extents->x = extents->y = 0; - extents->width = surface->width; - extents->height = surface->height; - } - - return &surface->base; -} - -static cairo_status_t -_cairo_gl_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_rectangle_int_t extents; - - *image_extra = NULL; - - extents.x = extents.y = 0; - extents.width = surface->width; - extents.height = surface->height; - - *image_out = (cairo_image_surface_t *) - _cairo_gl_surface_map_to_image (surface, &extents); - return (*image_out)->base.status; -} - -static void -_cairo_gl_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_int_status_t -_cairo_gl_surface_unmap_image (void *abstract_surface, - cairo_image_surface_t *image) -{ - cairo_int_status_t status; - - status = _cairo_gl_surface_draw_image (abstract_surface, image, - 0, 0, - image->width, image->height, - image->base.device_transform_inverse.x0, - image->base.device_transform_inverse.y0, - TRUE); - - cairo_surface_finish (&image->base); - cairo_surface_destroy (&image->base); - - return status; -} - -static cairo_bool_t -_cairo_gl_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_gl_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static cairo_status_t -_cairo_gl_surface_flush (void *abstract_surface, unsigned flags) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_gl_context_t *ctx; - - if (flags) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) || - (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && - ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) || - (ctx->current_target == surface)) - _cairo_gl_composite_flush (ctx); - - status = _cairo_gl_surface_resolve_multisampling (surface); - - return _cairo_gl_context_release (ctx, status); -} - -cairo_int_status_t -_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface) -{ - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - if (! surface->msaa_active) - return CAIRO_INT_STATUS_SUCCESS; - - if (surface->base.device == NULL) - return CAIRO_INT_STATUS_SUCCESS; - - /* GLES surfaces do not need explicit resolution. */ - if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES2) - return CAIRO_INT_STATUS_SUCCESS; - else if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES3 && - surface->content_in_texture) - return CAIRO_INT_STATUS_SUCCESS; - - if (! _cairo_gl_surface_is_texture (surface)) - return CAIRO_INT_STATUS_SUCCESS; - - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - -#if CAIRO_HAS_GLESV3_SURFACE - _cairo_gl_composite_flush (ctx); - ctx->current_target = NULL; - _cairo_gl_context_bind_framebuffer (ctx, surface, FALSE); - if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) - surface->content_in_texture = TRUE; - -#elif CAIRO_HAS_GL_SURFACE - ctx->current_target = surface; - _cairo_gl_context_bind_framebuffer (ctx, surface, FALSE); - -#else - ctx->current_target = surface; - -#endif - - status = _cairo_gl_context_release (ctx, status); - return status; -} - -static const cairo_compositor_t * -get_compositor (cairo_gl_surface_t *surface) -{ - cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; - return ctx->compositor; -} - -static cairo_int_status_t -_cairo_gl_surface_paint (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) -{ - /* simplify the common case of clearing the surface */ - if (clip == NULL) { - if (op == CAIRO_OPERATOR_CLEAR) - return _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); - else if (source->type == CAIRO_PATTERN_TYPE_SOLID && - (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) { - return _cairo_gl_surface_clear (surface, - &((cairo_solid_pattern_t *) source)->color); - } - } - - return _cairo_compositor_paint (get_compositor (surface), surface, - op, source, clip); -} - -static cairo_int_status_t -_cairo_gl_surface_mask (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip) -{ - return _cairo_compositor_mask (get_compositor (surface), surface, - op, source, mask, clip); -} - -static cairo_int_status_t -_cairo_gl_surface_stroke (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) -{ - return _cairo_compositor_stroke (get_compositor (surface), surface, - op, source, path, style, - ctm, ctm_inverse, tolerance, antialias, - clip); -} - -static cairo_int_status_t -_cairo_gl_surface_fill (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t*path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) -{ - return _cairo_compositor_fill (get_compositor (surface), surface, - op, source, path, - fill_rule, tolerance, antialias, - clip); -} - -static cairo_int_status_t -_cairo_gl_surface_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *font, - const cairo_clip_t *clip) -{ - return _cairo_compositor_glyphs (get_compositor (surface), surface, - op, source, glyphs, num_glyphs, font, - clip); -} - -static const cairo_surface_backend_t _cairo_gl_surface_backend = { - CAIRO_SURFACE_TYPE_GL, - _cairo_gl_surface_finish, - _cairo_default_context_create, - - _cairo_gl_surface_create_similar, - NULL, /* similar image */ - _cairo_gl_surface_map_to_image, - _cairo_gl_surface_unmap_image, - - _cairo_gl_surface_source, - _cairo_gl_surface_acquire_source_image, - _cairo_gl_surface_release_source_image, - NULL, /* snapshot */ - - NULL, /* copy_page */ - NULL, /* show_page */ - - _cairo_gl_surface_get_extents, - _cairo_image_surface_get_font_options, - - _cairo_gl_surface_flush, - NULL, /* mark_dirty_rectangle */ - - _cairo_gl_surface_paint, - _cairo_gl_surface_mask, - _cairo_gl_surface_stroke, - _cairo_gl_surface_fill, - NULL, /* fill/stroke */ - _cairo_gl_surface_glyphs, -}; diff --git a/src/cairo-gl-traps-compositor.c b/src/cairo-gl-traps-compositor.c deleted file mode 100644 index 7938c5b20..000000000 --- a/src/cairo-gl-traps-compositor.c +++ /dev/null @@ -1,531 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005,2010 Red Hat, Inc - * Copyright © 2011 Intel 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Benjamin Otte <otte@gnome.org> - * Carl Worth <cworth@cworth.org> - * Chris Wilson <chris@chris-wilson.co.uk> - * Eric Anholt <eric@anholt.net> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-composite-rectangles-private.h" -#include "cairo-compositor-private.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" -#include "cairo-image-surface-private.h" -#include "cairo-spans-compositor-private.h" -#include "cairo-surface-backend-private.h" -#include "cairo-surface-offset-private.h" - -static cairo_int_status_t -acquire (void *abstract_dst) -{ - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -release (void *abstract_dst) -{ - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -set_clip_region (void *_surface, - cairo_region_t *region) -{ - cairo_gl_surface_t *surface = _surface; - - surface->clip_region = region; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -draw_image_boxes (void *_dst, - cairo_image_surface_t *image, - cairo_boxes_t *boxes, - int dx, int dy) -{ - cairo_gl_surface_t *dst = _dst; - struct _cairo_boxes_chunk *chunk; - int i; - - for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - cairo_box_t *b = &chunk->base[i]; - int x = _cairo_fixed_integer_part (b->p1.x); - int y = _cairo_fixed_integer_part (b->p1.y); - int w = _cairo_fixed_integer_part (b->p2.x) - x; - int h = _cairo_fixed_integer_part (b->p2.y) - y; - cairo_status_t status; - - status = _cairo_gl_surface_draw_image (dst, image, - x + dx, y + dy, - w, h, - x, y, - TRUE); - if (unlikely (status)) - return status; - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -emit_aligned_boxes (cairo_gl_context_t *ctx, - const cairo_boxes_t *boxes) -{ - const struct _cairo_boxes_chunk *chunk; - cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx); - int i; - - for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); - int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); - int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); - int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); - emit (ctx, x1, y1, x2, y2); - } - } -} - -static cairo_int_status_t -fill_boxes (void *_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_boxes_t *boxes) -{ - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_solid_source (&setup, color); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - emit_aligned_boxes (ctx, boxes); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_int_status_t -composite_boxes (void *_dst, - cairo_operator_t op, - cairo_surface_t *abstract_src, - cairo_surface_t *abstract_mask, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - cairo_boxes_t *boxes, - const cairo_rectangle_int_t *extents) -{ - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_source_operand (&setup, - source_to_operand (abstract_src)); - _cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y); - - _cairo_gl_composite_set_mask_operand (&setup, - source_to_operand (abstract_mask)); - _cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - emit_aligned_boxes (ctx, boxes); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_int_status_t -composite (void *_dst, - cairo_operator_t op, - cairo_surface_t *abstract_src, - cairo_surface_t *abstract_mask, - 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_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_source_operand (&setup, - source_to_operand (abstract_src)); - _cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y); - - _cairo_gl_composite_set_mask_operand (&setup, - source_to_operand (abstract_mask)); - _cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - /* XXX clip */ - _cairo_gl_context_emit_rect (ctx, dst_x, dst_y, dst_x+width, dst_y+height); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_int_status_t -lerp (void *dst, - cairo_surface_t *src, - cairo_surface_t *mask, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - cairo_int_status_t status; - - /* we could avoid some repetition... */ - status = composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, - mask_x, mask_y, - 0, 0, - dst_x, dst_y, - width, height); - if (unlikely (status)) - return status; - - status = composite (dst, CAIRO_OPERATOR_ADD, src, mask, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -traps_to_operand (void *_dst, - const cairo_rectangle_int_t *extents, - cairo_antialias_t antialias, - cairo_traps_t *traps, - cairo_gl_operand_t *operand, - int dst_x, int dst_y) -{ - pixman_format_code_t pixman_format; - pixman_image_t *pixman_image; - cairo_surface_t *image, *mask; - cairo_surface_pattern_t pattern; - cairo_status_t status; - - pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1; - pixman_image = pixman_image_create_bits (pixman_format, - extents->width, - extents->height, - NULL, 0); - if (unlikely (pixman_image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _pixman_image_add_traps (pixman_image, extents->x, extents->y, traps); - image = _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); - if (unlikely (image->status)) { - pixman_image_unref (pixman_image); - return image->status; - } - - mask = _cairo_surface_create_scratch (_dst, - CAIRO_CONTENT_COLOR_ALPHA, - extents->width, - extents->height, - NULL); - if (unlikely (mask->status)) { - cairo_surface_destroy (image); - return mask->status; - } - - status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask, - (cairo_image_surface_t *)image, - 0, 0, - extents->width, extents->height, - 0, 0, - TRUE); - cairo_surface_destroy (image); - - if (unlikely (status)) - goto error; - - _cairo_pattern_init_for_surface (&pattern, mask); - cairo_matrix_init_translate (&pattern.base.matrix, - -extents->x+dst_x, -extents->y+dst_y); - pattern.base.filter = CAIRO_FILTER_NEAREST; - pattern.base.extend = CAIRO_EXTEND_NONE; - status = _cairo_gl_operand_init (operand, &pattern.base, _dst, - &_cairo_unbounded_rectangle, - &_cairo_unbounded_rectangle, - FALSE); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) - goto error; - - operand->texture.owns_surface = (cairo_gl_surface_t *)mask; - return CAIRO_STATUS_SUCCESS; - -error: - cairo_surface_destroy (mask); - return status; -} - -static cairo_int_status_t -composite_traps (void *_dst, - cairo_operator_t op, - cairo_surface_t *abstract_src, - int src_x, - int src_y, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_antialias_t antialias, - cairo_traps_t *traps) -{ - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_source_operand (&setup, - source_to_operand (abstract_src)); - _cairo_gl_operand_translate (&setup.src, -src_x-dst_x, -src_y-dst_y); - status = traps_to_operand (_dst, extents, antialias, traps, &setup.mask, dst_x, dst_y); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - /* XXX clip */ - _cairo_gl_context_emit_rect (ctx, - extents->x-dst_x, extents->y-dst_y, - extents->x-dst_x+extents->width, - extents->y-dst_y+extents->height); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_gl_surface_t * -tristrip_to_surface (void *_dst, - const cairo_rectangle_int_t *extents, - cairo_antialias_t antialias, - cairo_tristrip_t *strip) -{ - pixman_format_code_t pixman_format; - pixman_image_t *pixman_image; - cairo_surface_t *image, *mask; - cairo_status_t status; - - pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, - pixman_image = pixman_image_create_bits (pixman_format, - extents->width, - extents->height, - NULL, 0); - if (unlikely (pixman_image == NULL)) - return (cairo_gl_surface_t *)_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _pixman_image_add_tristrip (pixman_image, extents->x, extents->y, strip); - image = _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); - if (unlikely (image->status)) { - pixman_image_unref (pixman_image); - return (cairo_gl_surface_t *)image; - } - - mask = _cairo_surface_create_scratch (_dst, - CAIRO_CONTENT_COLOR_ALPHA, - extents->width, - extents->height, - NULL); - if (unlikely (mask->status)) { - cairo_surface_destroy (image); - return (cairo_gl_surface_t *)mask; - } - - status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask, - (cairo_image_surface_t *)image, - 0, 0, - extents->width, extents->height, - 0, 0, - TRUE); - cairo_surface_destroy (image); - if (unlikely (status)) { - cairo_surface_destroy (mask); - return (cairo_gl_surface_t*)_cairo_surface_create_in_error (status); - } - - return (cairo_gl_surface_t*)mask; -} - -static cairo_int_status_t -composite_tristrip (void *_dst, - cairo_operator_t op, - cairo_surface_t *abstract_src, - int src_x, - int src_y, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_antialias_t antialias, - cairo_tristrip_t *strip) -{ - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_gl_surface_t *mask; - cairo_int_status_t status; - - mask = tristrip_to_surface (_dst, extents, antialias, strip); - if (unlikely (mask->base.status)) - return mask->base.status; - - status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_source_operand (&setup, - source_to_operand (abstract_src)); - - //_cairo_gl_composite_set_mask_surface (&setup, mask, 0, 0); - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto FAIL; - - /* XXX clip */ - _cairo_gl_context_emit_rect (ctx, - dst_x, dst_y, - dst_x+extents->width, - dst_y+extents->height); - status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); - -FAIL: - _cairo_gl_composite_fini (&setup); - cairo_surface_destroy (&mask->base); - return status; -} - -static cairo_int_status_t -check_composite (const cairo_composite_rectangles_t *extents) -{ - if (! _cairo_gl_operator_is_supported (extents->op)) - return UNSUPPORTED ("unsupported operator"); - - return CAIRO_STATUS_SUCCESS; -} - -const cairo_compositor_t * -_cairo_gl_traps_compositor_get (void) -{ - static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; - static cairo_traps_compositor_t compositor; - - if (_cairo_atomic_init_once_enter(&once)) { - _cairo_traps_compositor_init (&compositor, &_cairo_fallback_compositor); - compositor.acquire = acquire; - compositor.release = release; - compositor.set_clip_region = set_clip_region; - compositor.pattern_to_surface = _cairo_gl_pattern_to_source; - compositor.draw_image_boxes = draw_image_boxes; - //compositor.copy_boxes = copy_boxes; - compositor.fill_boxes = fill_boxes; - compositor.check_composite = check_composite; - compositor.composite = composite; - compositor.lerp = lerp; - //compositor.check_composite_boxes = check_composite_boxes; - compositor.composite_boxes = composite_boxes; - //compositor.check_composite_traps = check_composite_traps; - compositor.composite_traps = composite_traps; - //compositor.check_composite_tristrip = check_composite_traps; - compositor.composite_tristrip = composite_tristrip; - compositor.check_composite_glyphs = _cairo_gl_check_composite_glyphs; - compositor.composite_glyphs = _cairo_gl_composite_glyphs; - - _cairo_atomic_init_once_leave(&once); - } - - return &compositor.base; -} diff --git a/src/cairo-gl.h b/src/cairo-gl.h deleted file mode 100644 index 7cd869c76..000000000 --- a/src/cairo-gl.h +++ /dev/null @@ -1,155 +0,0 @@ -/* Cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 Eric Anholt. - */ - -/* - * cairo-gl.h: - * - * The cairo-gl backend provides an implementation of possibly - * hardware-accelerated cairo rendering by targeting the OpenGL API. - * The goal of the cairo-gl backend is to provide better performance - * with equal functionality to cairo-image where possible. It does - * not directly provide for applying additional OpenGL effects to - * cairo surfaces. - * - * Cairo-gl allows interoperability with other GL rendering through GL - * context sharing. Cairo-gl surfaces are created in reference to a - * #cairo_device_t, which represents a GL context created by the user. - * When that GL context is created with its sharePtr set to another - * context (or vice versa), its objects (textures backing cairo-gl - * surfaces) can be accessed in the other OpenGL context. This allows - * cairo-gl to maintain its drawing state in one context while the - * user's 3D rendering occurs in the user's other context. - * - * However, as only one context can be current to a thread at a time, - * cairo-gl may make its context current to the thread on any cairo - * call which interacts with a cairo-gl surface or the cairo-gl - * device. As a result, the user must make their own context current - * between any cairo calls and their own OpenGL rendering. - **/ - -#ifndef CAIRO_GL_H -#define CAIRO_GL_H - -#include "cairo.h" - -#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE - -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_gl_surface_create (cairo_device_t *device, - cairo_content_t content, - int width, int height); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, - cairo_content_t content, - unsigned int tex, - int width, int height); -cairo_public void -cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height); - -cairo_public int -cairo_gl_surface_get_width (cairo_surface_t *abstract_surface); - -cairo_public int -cairo_gl_surface_get_height (cairo_surface_t *abstract_surface); - -cairo_public void -cairo_gl_surface_swapbuffers (cairo_surface_t *surface); - -cairo_public void -cairo_gl_device_set_thread_aware (cairo_device_t *device, - cairo_bool_t thread_aware); - -#if CAIRO_HAS_GLX_FUNCTIONS -#include <GL/glx.h> - -cairo_public cairo_device_t * -cairo_glx_device_create (Display *dpy, GLXContext gl_ctx); - -cairo_public Display * -cairo_glx_device_get_display (cairo_device_t *device); - -cairo_public GLXContext -cairo_glx_device_get_context (cairo_device_t *device); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_window (cairo_device_t *device, - Window win, - int width, int height); -#endif - -#if CAIRO_HAS_WGL_FUNCTIONS -#include <windows.h> - -cairo_public cairo_device_t * -cairo_wgl_device_create (HGLRC rc); - -cairo_public HGLRC -cairo_wgl_device_get_context (cairo_device_t *device); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_dc (cairo_device_t *device, - HDC dc, - int width, - int height); -#endif - -#if CAIRO_HAS_EGL_FUNCTIONS -#include <EGL/egl.h> - -cairo_public cairo_device_t * -cairo_egl_device_create (EGLDisplay dpy, EGLContext egl); - -cairo_public cairo_surface_t * -cairo_gl_surface_create_for_egl (cairo_device_t *device, - EGLSurface egl, - int width, - int height); - -cairo_public EGLDisplay -cairo_egl_device_get_display (cairo_device_t *device); - -cairo_public EGLSurface -cairo_egl_device_get_context (cairo_device_t *device); - -#endif - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_GL_SURFACE */ -# error Cairo was not compiled with support for the GL backend -#endif /* CAIRO_HAS_GL_SURFACE */ - -#endif /* CAIRO_GL_H */ diff --git a/src/cairo-glx-context.c b/src/cairo-glx-context.c deleted file mode 100644 index 66f5a0d1b..000000000 --- a/src/cairo-glx-context.c +++ /dev/null @@ -1,324 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005 Red Hat, Inc - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-error-private.h" - -#include <X11/Xutil.h> - -/* XXX needs hooking into XCloseDisplay() */ - -typedef struct _cairo_glx_context { - cairo_gl_context_t base; - - Display *display; - Window dummy_window; - GLXContext context; - - GLXDrawable previous_drawable; - GLXContext previous_context; - - cairo_bool_t has_multithread_makecurrent; -} cairo_glx_context_t; - -typedef struct _cairo_glx_surface { - cairo_gl_surface_t base; - - Window win; -} cairo_glx_surface_t; - -static cairo_bool_t -_context_acquisition_changed_glx_state (cairo_glx_context_t *ctx, - GLXDrawable current_drawable) -{ - return ctx->previous_drawable != current_drawable || - ctx->previous_context != ctx->context; -} - -static GLXDrawable -_glx_get_current_drawable (cairo_glx_context_t *ctx) -{ - if (ctx->base.current_target == NULL || - _cairo_gl_surface_is_texture (ctx->base.current_target)) { - return ctx->dummy_window; - } - - return ((cairo_glx_surface_t *) ctx->base.current_target)->win; -} - -static void -_glx_query_current_state (cairo_glx_context_t * ctx) -{ - ctx->previous_drawable = glXGetCurrentDrawable (); - ctx->previous_context = glXGetCurrentContext (); - - /* If any of the values were none, assume they are all none. Not all - drivers seem well behaved when it comes to using these values across - multiple threads. */ - if (ctx->previous_drawable == None || - ctx->previous_context == None) { - ctx->previous_drawable = None; - ctx->previous_context = None; - } -} - -static void -_glx_acquire (void *abstract_ctx) -{ - cairo_glx_context_t *ctx = abstract_ctx; - GLXDrawable current_drawable = _glx_get_current_drawable (ctx); - - _glx_query_current_state (ctx); - if (!_context_acquisition_changed_glx_state (ctx, current_drawable)) - return; - - glXMakeCurrent (ctx->display, current_drawable, ctx->context); -} - -static void -_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) -{ - cairo_glx_context_t *ctx = abstract_ctx; - cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; - - /* Set the window as the target of our context. */ - glXMakeCurrent (ctx->display, surface->win, ctx->context); -} - -static void -_glx_release (void *abstract_ctx) -{ - cairo_glx_context_t *ctx = abstract_ctx; - - if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware || - !_context_acquisition_changed_glx_state (ctx, - _glx_get_current_drawable (ctx))) { - return; - } - - glXMakeCurrent (ctx->display, None, None); -} - -static void -_glx_swap_buffers (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_glx_context_t *ctx = abstract_ctx; - cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; - - glXSwapBuffers (ctx->display, surface->win); -} - -static void -_glx_destroy (void *abstract_ctx) -{ - cairo_glx_context_t *ctx = abstract_ctx; - - if (ctx->dummy_window != None) - XDestroyWindow (ctx->display, ctx->dummy_window); - - glXMakeCurrent (ctx->display, None, None); -} - -static cairo_status_t -_glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy) -{ - int attr[3] = { GLX_FBCONFIG_ID, 0, None }; - GLXFBConfig *config; - XVisualInfo *vi; - Colormap cmap; - XSetWindowAttributes swa; - Window win = None; - int cnt; - - /* Create a dummy window created for the target GLX context that we can - * use to query the available GL/GLX extensions. - */ - glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]); - - cnt = 0; - config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt); - if (unlikely (cnt == 0)) - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - - vi = glXGetVisualFromFBConfig (dpy, config[0]); - XFree (config); - - if (unlikely (vi == NULL)) - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - - cmap = XCreateColormap (dpy, - RootWindow (dpy, vi->screen), - vi->visual, - AllocNone); - swa.colormap = cmap; - swa.border_pixel = 0; - win = XCreateWindow (dpy, RootWindow (dpy, vi->screen), - -1, -1, 1, 1, 0, - vi->depth, - InputOutput, - vi->visual, - CWBorderPixel | CWColormap, &swa); - XFreeColormap (dpy, cmap); - XFree (vi); - - XFlush (dpy); - if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) { - XDestroyWindow (dpy, win); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - *dummy = win; - return CAIRO_STATUS_SUCCESS; -} - -cairo_device_t * -cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) -{ - cairo_glx_context_t *ctx; - cairo_status_t status; - Window dummy = None; - const char *glx_extensions; - - ctx = calloc (1, sizeof (cairo_glx_context_t)); - if (unlikely (ctx == NULL)) - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - - /* glx_dummy_window will call glXMakeCurrent, so we need to - * query the current state of the context now. */ - _glx_query_current_state (ctx); - - status = _glx_dummy_window (dpy, gl_ctx, &dummy); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - ctx->display = dpy; - ctx->dummy_window = dummy; - ctx->context = gl_ctx; - - ctx->base.acquire = _glx_acquire; - ctx->base.release = _glx_release; - ctx->base.make_current = _glx_make_current; - ctx->base.swap_buffers = _glx_swap_buffers; - ctx->base.destroy = _glx_destroy; - - status = _cairo_gl_dispatch_init (&ctx->base.dispatch, - (cairo_gl_get_proc_addr_func_t) glXGetProcAddress); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - status = _cairo_gl_context_init (&ctx->base); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy)); - if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) { - ctx->has_multithread_makecurrent = TRUE; - } - - ctx->base.release (ctx); - - return &ctx->base.base; -} - -Display * -cairo_glx_device_get_display (cairo_device_t *device) -{ - cairo_glx_context_t *ctx; - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return NULL; - } - - ctx = (cairo_glx_context_t *) device; - - return ctx->display; -} - -GLXContext -cairo_glx_device_get_context (cairo_device_t *device) -{ - cairo_glx_context_t *ctx; - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return NULL; - } - - ctx = (cairo_glx_context_t *) device; - - return ctx->context; -} - -cairo_surface_t * -cairo_gl_surface_create_for_window (cairo_device_t *device, - Window win, - int width, - int height) -{ - cairo_glx_surface_t *surface; - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - if (width <= 0 || height <= 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - surface = calloc (1, sizeof (cairo_glx_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (device, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - surface->win = win; - - return &surface->base.base; -} diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index a8c67e718..8a253468d 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1145,7 +1145,7 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, } _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); - if (source->type == CAIRO_PATTERN_TYPE_SOLID && + if (source->type == CAIRO_PATTERN_TYPE_SOLID && !source->is_foreground_marker && mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && _cairo_operator_bounded_by_source (op)) { @@ -1748,11 +1748,12 @@ void _cairo_gstate_set_font_options (cairo_gstate_t *gstate, const cairo_font_options_t *options) { - if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0) + if (_cairo_font_options_compare (options, &gstate->font_options)) return; _cairo_gstate_unset_scaled_font (gstate); + _cairo_font_options_fini (&gstate->font_options); _cairo_font_options_init_copy (&gstate->font_options, options); } @@ -1760,7 +1761,8 @@ void _cairo_gstate_get_font_options (cairo_gstate_t *gstate, cairo_font_options_t *options) { - *options = gstate->font_options; + _cairo_font_options_fini (options); + _cairo_font_options_init_copy (options, &gstate->font_options); } cairo_status_t diff --git a/src/cairo-image-info.c b/src/cairo-image-info.c index f207ae887..9693e487f 100644 --- a/src/cairo-image-info.c +++ b/src/cairo-image-info.c @@ -162,9 +162,15 @@ static const unsigned char _jpx_signature[] = { }; static const unsigned char * -_jpx_next_box (const unsigned char *p) +_jpx_next_box (const unsigned char *p, const unsigned char *end) { - return p + get_unaligned_be32 (p); + if (p + 4 < end) { + uint32_t length = get_unaligned_be32 (p); + if (p + length < end) + return p + length; + } + + return end; } static const unsigned char * @@ -193,19 +199,25 @@ _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); + p = _jpx_next_box (p, end); } return NULL; } -static void -_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) +static cairo_int_status_t +_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info, const unsigned char *end) { + if (p + 11 >= end) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + info->height = get_unaligned_be32 (p); info->width = get_unaligned_be32 (p + 4); info->num_components = (p[8] << 8) + p[9]; info->bits_per_component = p[10]; + + return CAIRO_STATUS_SUCCESS; } cairo_int_status_t @@ -227,7 +239,7 @@ _cairo_image_info_get_jpx_info (cairo_image_info_t *info, if (! _jpx_match_box (p, end, JPX_FILETYPE)) return CAIRO_INT_STATUS_UNSUPPORTED; - p = _jpx_next_box (p); + p = _jpx_next_box (p, end); /* Locate the JP2 header box. */ p = _jpx_find_box (p, end, JPX_JP2_HEADER); @@ -242,9 +254,7 @@ _cairo_image_info_get_jpx_info (cairo_image_info_t *info, /* Get the image info */ p = _jpx_get_box_contents (p); - _jpx_extract_info (p, info); - - return CAIRO_STATUS_SUCCESS; + return _jpx_extract_info (p, info, end); } /* PNG (image/png) @@ -348,6 +358,8 @@ _jbig2_get_next_segment (const unsigned char *p, num_segs = p[0] >> 5; if (num_segs == 7) { + if (p + 4 >= end) + return NULL; num_segs = get_unaligned_be32 (p) & 0x1fffffff; ref_seg_bytes = 4 + ((num_segs + 1)/8); } else { diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c index c56845ab2..aafdaeded 100644 --- a/src/cairo-image-source.c +++ b/src/cairo-image-source.c @@ -1207,6 +1207,8 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, clone = _cairo_image_surface_create_with_content (source->content, limit.width, limit.height); + if (dst->base.foreground_source) + clone->foreground_source = cairo_pattern_reference (dst->base.foreground_source); } m = NULL; @@ -1223,7 +1225,9 @@ _pixman_image_for_recording (cairo_image_surface_t *dst, /* Handle recursion by returning future reads from the current image */ proxy = attach_proxy (source, clone); - status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL); + status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL, FALSE); + if (clone->foreground_used) + dst->base.foreground_used = clone->foreground_used; detach_proxy (source, proxy); if (unlikely (status)) { cairo_surface_destroy (clone); diff --git a/src/cairo-lzw.c b/src/cairo-lzw.c index f27b3c338..e17cdfc1c 100644 --- a/src/cairo-lzw.c +++ b/src/cairo-lzw.c @@ -369,10 +369,10 @@ _cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out) * lookup. */ _lzw_buf_store_bits (&buf, prev, code_bits); - if (bytes_remaining == 0) - break; + if (likely (slot != NULL)) + LZW_SYMBOL_SET_CODE (*slot, code_next, prev, next); - LZW_SYMBOL_SET_CODE (*slot, code_next++, prev, next); + code_next++; if (code_next > LZW_BITS_BOUNDARY(code_bits)) { @@ -384,6 +384,9 @@ _cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out) code_next = LZW_CODE_FIRST; } } + + if (bytes_remaining == 0) + break; } /* The LZW footer is an end-of-data code. */ diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index f3cf684c9..e8fd097ae 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -304,6 +304,7 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) cairo_matrix_multiply (matrix, &tmp, matrix); } +slim_hidden_def (cairo_matrix_rotate); /** * cairo_matrix_multiply: @@ -372,15 +373,10 @@ _cairo_matrix_multiply (cairo_matrix_t *r, * the returned vector is as follows: * * <programlisting> - * dx2 = dx1 * a + dy1 * c; - * dy2 = dx1 * b + dy1 * d; + * dx_new = xx * dx + xy * dy; + * dy_new = yx * dx + yy * dy; * </programlisting> * - * Affine transformations are position invariant, so the same vector - * always transforms to the same vector. If (@x1,@y1) transforms - * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to - * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. - * * Since: 1.0 **/ void diff --git a/src/cairo-mesh-pattern-rasterizer.c b/src/cairo-mesh-pattern-rasterizer.c index e7f0db666..f47800c8a 100644 --- a/src/cairo-mesh-pattern-rasterizer.c +++ b/src/cairo-mesh-pattern-rasterizer.c @@ -659,7 +659,7 @@ draw_bezier_curve (unsigned char *data, int width, int height, int stride, * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * vshift is log2(n) if n is the number of desired steps - * p[i][j], p[i][j] are the the nodes of the Bezier patch + * p[i][j], p[i][j] are the nodes of the Bezier patch * col[i][j] is the j-th color component of the i-th corner * * Output: data will be changed to have the requested patch drawn in diff --git a/src/cairo-misc.c b/src/cairo-misc.c index bf8a62730..6f6f9937e 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -176,6 +176,8 @@ cairo_status_to_string (cairo_status_t status) return "invalid tag name, attributes, or nesting"; case CAIRO_STATUS_DWRITE_ERROR: return "Window Direct Write error"; + case CAIRO_STATUS_SVG_FONT_ERROR: + return "error occured while rendering an OpenType-SVG font"; default: case CAIRO_STATUS_LAST_STATUS: return "<unknown error status>"; @@ -888,6 +890,33 @@ _cairo_strtod (const char *nptr, char **endptr) } #endif +#ifndef HAVE_STRNDUP +char * +_cairo_strndup (const char *s, size_t n) +{ + const char *end; + size_t len; + char *sdup; + + if (s == NULL) + return NULL; + + end = memchr (s, 0, n); + if (end) + len = end - s; + else + len = n; + + sdup = (char *) _cairo_malloc (len + 1); + if (sdup != NULL) { + memcpy (sdup, s, len); + sdup[len] = '\0'; + } + + return sdup; +} +#endif + /** * _cairo_fopen: * @filename: filename to open @@ -949,15 +978,6 @@ _cairo_fopen (const char *filename, const char *mode, FILE **file_out) #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include <windows.h> #include <io.h> diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h index de0c9b21b..b9430beef 100644 --- a/src/cairo-mutex-impl-private.h +++ b/src/cairo-mutex-impl-private.h @@ -179,15 +179,6 @@ #elif defined(_WIN32) /******************************************************/ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - # include <windows.h> typedef CRITICAL_SECTION cairo_mutex_impl_t; diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h index 70d566ebb..af5cc0517 100644 --- a/src/cairo-mutex-list-private.h +++ b/src/cairo-mutex-list-private.h @@ -64,10 +64,6 @@ CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex) #endif -#if CAIRO_HAS_GL_SURFACE -CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) -#endif - #if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER) CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex) #endif diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index 826c9cf8e..7305b52ca 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -259,11 +259,13 @@ void _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length) { - if (length == 0) + if (length == 0 || stream->status) return; - if (stream->status) + if (stream->closed) { + stream->status = CAIRO_STATUS_WRITE_ERROR; return; + } stream->status = stream->write_func (stream, data, length); stream->position += length; @@ -278,9 +280,6 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, char buffer[2]; unsigned int i, column; - if (stream->status) - return; - for (i = 0, column = 0; i < length; i++, column++) { if (column == 38) { _cairo_output_stream_write (stream, "\n", 1); @@ -407,9 +406,6 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, int length_modifier, width; cairo_bool_t var_width; - if (stream->status) - return; - f = fmt; p = buffer; while (*f != '\0') { @@ -786,9 +782,6 @@ _cairo_memory_stream_copy (cairo_output_stream_t *base, { memory_stream_t *stream = (memory_stream_t *) base; - if (dest->status) - return; - if (base->status) { dest->status = base->status; return; diff --git a/src/cairo-paginated-private.h b/src/cairo-paginated-private.h index 89f1c99a2..cc8fd1c7b 100644 --- a/src/cairo-paginated-private.h +++ b/src/cairo-paginated-private.h @@ -178,7 +178,7 @@ _cairo_surface_is_paginated (cairo_surface_t *surface); cairo_private cairo_status_t _cairo_paginated_surface_set_size (cairo_surface_t *surface, - int width, - int height); + double width, + double height); #endif /* CAIRO_PAGINATED_H */ diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index a3f7cd9b2..e079a9a28 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc @@ -166,8 +167,8 @@ _cairo_paginated_surface_get_recording (cairo_surface_t *surface) cairo_status_t _cairo_paginated_surface_set_size (cairo_surface_t *surface, - int width, - int height) + double width, + double height) { cairo_paginated_surface_t *paginated_surface; cairo_status_t status; @@ -401,11 +402,12 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_surface_t *analysis; cairo_int_status_t status; cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; + unsigned int regions_id = 0; if (unlikely (surface->target->status)) return surface->target->status; - analysis = _cairo_analysis_surface_create (surface->target); + analysis = _cairo_analysis_surface_create (surface->target, TRUE); if (unlikely (analysis->status)) return _cairo_surface_set_error (surface->target, analysis->status); @@ -414,21 +416,26 @@ _paint_page (cairo_paginated_surface_t *surface) if (unlikely (status)) goto FAIL; + status = _cairo_recording_surface_region_array_attach (surface->recording_surface, ®ions_id); + if (status) + goto FAIL; + status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, + regions_id, NULL, analysis, FALSE); if (status) goto FAIL; assert (analysis->status == CAIRO_STATUS_SUCCESS); - if (surface->backend->set_bounding_box) { - cairo_box_t bbox; + if (surface->backend->set_bounding_box) { + cairo_box_t bbox; - _cairo_analysis_surface_get_bounding_box (analysis, &bbox); - status = surface->backend->set_bounding_box (surface->target, &bbox); - if (unlikely (status)) - goto FAIL; - } + _cairo_analysis_surface_get_bounding_box (analysis, &bbox); + status = surface->backend->set_bounding_box (surface->target, &bbox); + if (unlikely (status)) + goto FAIL; + } if (surface->backend->set_fallback_images_required) { cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis); @@ -467,6 +474,7 @@ _paint_page (cairo_paginated_surface_t *surface) goto FAIL; status = _cairo_recording_surface_replay_region (surface->recording_surface, + regions_id, NULL, surface->target, CAIRO_RECORDING_REGION_NATIVE); @@ -525,6 +533,9 @@ _paint_page (cairo_paginated_surface_t *surface) } FAIL: + if (regions_id) + _cairo_recording_surface_region_array_remove (surface->recording_surface, regions_id); + cairo_surface_destroy (analysis); return _cairo_surface_set_error (surface->target, status); @@ -745,6 +756,14 @@ _cairo_paginated_surface_tag (void *abstract_surface, begin, tag_name, attributes); } +static cairo_bool_t +_cairo_paginated_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + return TRUE; +} + static cairo_surface_t * _cairo_paginated_surface_snapshot (void *abstract_other) { @@ -800,4 +819,5 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { _cairo_paginated_surface_show_text_glyphs, _cairo_paginated_surface_get_supported_mime_types, _cairo_paginated_surface_tag, + _cairo_paginated_surface_supports_color_glyph, }; diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c index 3f7c49802..44b6675e8 100644 --- a/src/cairo-path-stroke-polygon.c +++ b/src/cairo-path-stroke-polygon.c @@ -399,16 +399,16 @@ outer_close (struct stroker *stroker, switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: - /* construct a fan around the common midpoint */ if ((in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) { + /* construct a fan around the common midpoint */ add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, clockwise, outer); - break; - } - /* else fall through */ + } /* else: bevel join */ + break; + case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ @@ -587,10 +587,14 @@ outer_join (struct stroker *stroker, switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: - /* construct a fan around the common midpoint */ - add_fan (stroker, - &in->dev_vector, &out->dev_vector, &in->point, - clockwise, outer); + if ((in->dev_slope.x * out->dev_slope.x + + in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) + { + /* construct a fan around the common midpoint */ + add_fan (stroker, + &in->dev_vector, &out->dev_vector, &in->point, + clockwise, outer); + } /* else: bevel join */ break; case CAIRO_LINE_JOIN_MITER: @@ -1291,17 +1295,72 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, stroker.ctm_inverse = ctm_inverse; stroker.tolerance = tolerance; stroker.half_line_width = style->line_width / 2.; - /* To test whether we need to join two segments of a spline using - * a round-join or a bevel-join, we can inspect the angle between the - * two segments. If the difference between the chord distance - * (half-line-width times the cosine of the bisection angle) and the - * half-line-width itself is greater than tolerance then we need to - * inject a point. + + /* If `CAIRO_LINE_JOIN_ROUND` is selected and a joint's `arc height` + * is greater than `tolerance` then two segments are joined with + * round-join, otherwise bevel-join is used. + * + * (See https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/372#note_1698225 + * for an illustration.) + * + * `Arc height` is the distance from the center of arc's chord to + * the center of the arc. It is also the difference of arc's radius + * and the "distance from a point where segments are joined to the + * chord" (distance to the chord). Arc's radius is the half of a line + * width and the "distance to the chord" is equal to "half of a line width" + * times `cos(half the angle between segment vectors)`. So + * + * arc_height = w/2 - w/2 * cos(phi/2), + * + * where `w/2` is the "half of a line width". + * + * Using the double angle cosine formula we can express the `cos(phi/2)` + * with just `cos(phi)` which is also the dot product of segments' + * unit vectors. + * + * cos(phi/2) = sqrt ( (1 + cos(phi)) / 2 ); + * cos(phi/2) is in [0; 1] range, cannot be negative; + * + * cos(phi) = a . b = (ax * bx + ay * by), + * + * where `a` and `b` are unit vectors of the segments to be joined. + * + * Since `arc height` should be greater than the `tolerance` to produce + * a round-join we can write + * + * w/2 * (1 - cos(phi/2)) > tolerance; + * 1 - tolerance / (w/2) > cos(phi/2); [!] + * + * which can be rewritten with the above double angle formula to + * + * cos(phi) < 2 * ( 1 - tolerance / (w/2) )^2 - 1, + * + * [!] Note that `w/2` is in [tolerance; +inf] range, since `cos(phi/2)` + * cannot be negative. The left part of the above inequality is the + * dot product and the right part is the `spline_cusp_tolerance`: + * + * (ax * bx + ay * by) < spline_cusp_tolerance. + * + * In the code below only the `spline_cusp_tolerance` is calculated. + * The dot product is calculated later, in the condition expression + * itself. "Half of a line width" must be scaled with CTM for tolerance + * condition to be properly met. Also, since `arch height` cannot exceed + * the "half of a line width" and since `cos(phi/2)` cannot be negative, + * when `tolerance` is greater than the "half of a line width" the + * bevel-join should be produced. */ - stroker.spline_cusp_tolerance = 1 - tolerance / stroker.half_line_width; - stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance; - stroker.spline_cusp_tolerance *= 2; - stroker.spline_cusp_tolerance -= 1; + double scaled_hlw = hypot(stroker.half_line_width * ctm->xx, + stroker.half_line_width * ctm->yx); + + if (scaled_hlw <= tolerance) { + stroker.spline_cusp_tolerance = -1.0; + } else { + stroker.spline_cusp_tolerance = 1 - tolerance / scaled_hlw; + stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance; + stroker.spline_cusp_tolerance *= 2; + stroker.spline_cusp_tolerance -= 1; + } + stroker.ctm_det_positive = _cairo_matrix_compute_determinant (ctm) >= 0.0; diff --git a/src/cairo-path-stroke-traps.c b/src/cairo-path-stroke-traps.c index eab978235..4eabf6583 100644 --- a/src/cairo-path-stroke-traps.c +++ b/src/cairo-path-stroke-traps.c @@ -102,17 +102,29 @@ stroker_init (struct stroker *stroker, stroker->tolerance = tolerance; stroker->traps = traps; - /* To test whether we need to join two segments of a spline using - * a round-join or a bevel-join, we can inspect the angle between the - * two segments. If the difference between the chord distance - * (half-line-width times the cosine of the bisection angle) and the - * half-line-width itself is greater than tolerance then we need to - * inject a point. + /* If `CAIRO_LINE_JOIN_ROUND` is selected and a joint's `arc height` + * is greater than `tolerance` then two segments are joined with + * round-join, otherwise bevel-join is used. + * + * `Arc height` is the difference of the "half of a line width" and + * the "half of a line width" times `cos(half the angle between segment vectors)`. + * + * See detailed description in the `_cairo_path_fixed_stroke_to_polygon()` + * function in the `cairo-path-stroke-polygon.c` file or follow the + * https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/372#note_1698225 + * link to see the detailed description with an illustration. */ - stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width; - stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance; - stroker->spline_cusp_tolerance *= 2; - stroker->spline_cusp_tolerance -= 1; + double scaled_hlw = hypot(stroker->half_line_width * ctm->xx, + stroker->half_line_width * ctm->yx); + + if (scaled_hlw <= tolerance) { + stroker->spline_cusp_tolerance = -1.0; + } else { + stroker->spline_cusp_tolerance = 1 - tolerance / scaled_hlw; + stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance; + stroker->spline_cusp_tolerance *= 2; + stroker->spline_cusp_tolerance -= 1; + } stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h index f6138fb70..d061b39c4 100644 --- a/src/cairo-pattern-private.h +++ b/src/cairo-pattern-private.h @@ -72,7 +72,7 @@ struct _cairo_pattern { cairo_filter_t filter; cairo_extend_t extend; cairo_bool_t has_component_alpha; - cairo_bool_t is_userfont_foreground; + cairo_bool_t is_foreground_marker; cairo_matrix_t matrix; double opacity; @@ -87,6 +87,12 @@ typedef struct _cairo_surface_pattern { cairo_pattern_t base; cairo_surface_t *surface; + + /* This field is only used by the wrapper surface for retreiving + * the region id from the target during create regions and passing + * the region id to the target surface during playback. + */ + unsigned int region_array_id; } cairo_surface_pattern_t; typedef struct _cairo_gradient_stop { @@ -234,6 +240,9 @@ _cairo_pattern_fini (cairo_pattern_t *pattern); cairo_private cairo_pattern_t * _cairo_pattern_create_solid (const cairo_color_t *color); +cairo_private cairo_pattern_t * +_cairo_pattern_create_foreground_marker (void); + cairo_private void _cairo_pattern_transform (cairo_pattern_t *pattern, const cairo_matrix_t *ctm_inverse); diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 6bd3edfd8..2c0ba31f8 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -76,7 +76,7 @@ static const cairo_solid_pattern_t _cairo_pattern_nil = { CAIRO_FILTER_DEFAULT, /* filter */ CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ FALSE, /* has component alpha */ - FALSE, /* is_userfont_foreground */ + FALSE, /* is_foreground_marker */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ } @@ -93,7 +93,7 @@ static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { CAIRO_FILTER_DEFAULT, /* filter */ CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ FALSE, /* has component alpha */ - FALSE, /* is_userfont_foreground */ + FALSE, /* is_foreground_marker */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ } @@ -110,7 +110,7 @@ const cairo_solid_pattern_t _cairo_pattern_black = { CAIRO_FILTER_NEAREST, /* filter */ CAIRO_EXTEND_REPEAT, /* extend */ FALSE, /* has component alpha */ - FALSE, /* is_userfont_foreground */ + FALSE, /* is_foreground_marker */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ }, @@ -128,7 +128,7 @@ const cairo_solid_pattern_t _cairo_pattern_clear = { CAIRO_FILTER_NEAREST, /* filter */ CAIRO_EXTEND_REPEAT, /* extend */ FALSE, /* has component alpha */ - FALSE, /* is_userfont_foreground */ + FALSE, /* is_foreground_marker */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ }, @@ -146,7 +146,7 @@ const cairo_solid_pattern_t _cairo_pattern_white = { CAIRO_FILTER_NEAREST, /* filter */ CAIRO_EXTEND_REPEAT, /* extend */ FALSE, /* has component alpha */ - FALSE, /* is_userfont_foreground */ + FALSE, /* is_foreground_marker */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ }, @@ -238,7 +238,7 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) pattern->opacity = 1.0; pattern->has_component_alpha = FALSE; - pattern->is_userfont_foreground = FALSE; + pattern->is_foreground_marker = FALSE; cairo_matrix_init_identity (&pattern->matrix); @@ -561,6 +561,7 @@ _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE); pattern->surface = cairo_surface_reference (surface); + pattern->region_array_id = 0; } static void @@ -624,6 +625,14 @@ _cairo_pattern_create_solid (const cairo_color_t *color) } cairo_pattern_t * +_cairo_pattern_create_foreground_marker (void) +{ + cairo_pattern_t *pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); + pattern->is_foreground_marker = TRUE; + return pattern; +} + +cairo_pattern_t * _cairo_pattern_create_in_error (cairo_status_t status) { cairo_pattern_t *pattern; @@ -681,6 +690,8 @@ slim_hidden_def (cairo_pattern_create_rgb); * 1. If the values passed in are outside that range, they will be * clamped. * + * The color is specified in the same way as in cairo_set_source_rgb(). + * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when @@ -810,6 +821,7 @@ cairo_pattern_create_linear (double x0, double y0, double x1, double y1) return &pattern->base.base; } +slim_hidden_def (cairo_pattern_create_linear); /** * cairo_pattern_create_radial: @@ -864,6 +876,7 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0, return &pattern->base.base; } +slim_hidden_def (cairo_pattern_create_radial); /* This order is specified in the diagram in the documentation for * cairo_pattern_create_mesh() */ @@ -1044,6 +1057,7 @@ cairo_pattern_create_mesh (void) return &pattern->base; } +slim_hidden_def (cairo_pattern_create_mesh); /** * cairo_pattern_reference: @@ -1091,6 +1105,7 @@ cairo_pattern_get_type (cairo_pattern_t *pattern) { return pattern->type; } +slim_hidden_def (cairo_pattern_get_type); /** * cairo_pattern_status: @@ -1278,7 +1293,7 @@ cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern) for (i = 0; i < 4; i++) mesh->has_color[i] = FALSE; } - +slim_hidden_def (cairo_mesh_pattern_begin_patch); static void _calc_control_point (cairo_mesh_patch_t *patch, int control_point) @@ -1395,6 +1410,7 @@ cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern) mesh->current_patch = NULL; } +slim_hidden_def (cairo_mesh_pattern_end_patch); /** * cairo_mesh_pattern_curve_to: @@ -2115,6 +2131,7 @@ cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) pattern->extend = extend; _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND); } +slim_hidden_def (cairo_pattern_set_extend); /** * cairo_pattern_get_extend: @@ -3422,9 +3439,10 @@ use_bilinear(double x, double y, double t) /** * _cairo_pattern_analyze_filter: * @pattern: surface pattern - * Returns: the optimized #cairo_filter_t to use with @pattern. * * Possibly optimize the filter to a simpler value depending on transformation + * + * Returns: the optimized #cairo_filter_t to use with @pattern. **/ cairo_filter_t _cairo_pattern_analyze_filter (const cairo_pattern_t *pattern) @@ -4160,6 +4178,8 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) * * Gets the solid color for a solid color pattern. * + * Note that the color and alpha values are not premultiplied. + * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid * color pattern. @@ -4193,6 +4213,7 @@ cairo_pattern_get_rgba (cairo_pattern_t *pattern, return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_pattern_get_rgba); /** * cairo_pattern_get_surface: @@ -4242,6 +4263,8 @@ cairo_pattern_get_surface (cairo_pattern_t *pattern, * where n is the number returned * by cairo_pattern_get_color_stop_count(). * + * Note that the color and alpha values are not premultiplied. + * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @index is not valid for the given pattern. If the pattern is * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is @@ -4549,6 +4572,8 @@ slim_hidden_def (cairo_mesh_pattern_get_path); * Valid values for @corner_num are from 0 to 3 and identify the * corners as explained in cairo_pattern_create_mesh(). * + * Note that the color and alpha values are not premultiplied. + * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @patch_num or @corner_num is not valid for @pattern. If * @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index 87f5ffa25..c97affe6b 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -95,6 +95,7 @@ typedef struct _cairo_pdf_source_surface_entry { typedef struct _cairo_pdf_source_surface { cairo_pattern_type_t type; cairo_surface_t *surface; + unsigned int region_id; cairo_pattern_t *raster_pattern; cairo_pdf_source_surface_entry_t *hash_entry; } cairo_pdf_source_surface_t; @@ -257,6 +258,13 @@ typedef struct _cairo_pdf_interchange { } cairo_pdf_interchange_t; +typedef struct _cairo_pdf_color_glyph { + cairo_hash_entry_t base; + cairo_scaled_font_t *scaled_font; + unsigned long glyph_index; + cairo_bool_t supported; +} cairo_pdf_color_glyph_t; + /* pdf surface data */ typedef struct _cairo_pdf_surface cairo_pdf_surface_t; @@ -279,14 +287,15 @@ struct _cairo_pdf_surface { cairo_array_t pages; cairo_array_t rgb_linear_functions; cairo_array_t alpha_linear_functions; - cairo_array_t page_patterns; - cairo_array_t page_surfaces; - cairo_array_t doc_surfaces; + cairo_array_t page_patterns; /* cairo_pdf_pattern_t */ + cairo_array_t page_surfaces; /* cairo_pdf_source_surface_t */ + cairo_array_t doc_surfaces; /* cairo_pdf_source_surface_t */ cairo_hash_table_t *all_surfaces; cairo_array_t smask_groups; cairo_array_t knockout_group; cairo_array_t jbig2_global; cairo_array_t page_heights; + cairo_hash_table_t *color_glyphs; cairo_scaled_font_subsets_t *font_subsets; cairo_array_t fonts; @@ -334,11 +343,13 @@ struct _cairo_pdf_surface { cairo_pdf_operators_t pdf_operators; cairo_paginated_mode_t paginated_mode; + cairo_bool_t type3_replay; cairo_bool_t select_pattern_gstate_saved; cairo_bool_t force_fallbacks; cairo_operator_t current_operator; + cairo_bool_t reset_gs_required; cairo_bool_t current_pattern_is_solid_color; cairo_bool_t current_color_is_stroke; double current_color_red; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 9b2b93252..2b1bf72e4 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -54,6 +54,7 @@ #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-image-info-private.h" +#include "cairo-recording-surface-inline.h" #include "cairo-recording-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-paginated-private.h" @@ -132,10 +133,10 @@ * The PDF surface is used to render cairo graphics to Adobe * PDF files and is a multi-page vector surface backend. * - * The following mime types are supported: %CAIRO_MIME_TYPE_JPEG, - * %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_UNIQUE_ID, - * %CAIRO_MIME_TYPE_JBIG2, %CAIRO_MIME_TYPE_JBIG2_GLOBAL, - * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, + * The following mime types are supported on source patterns: + * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_JP2, + * %CAIRO_MIME_TYPE_UNIQUE_ID, %CAIRO_MIME_TYPE_JBIG2, + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. * * # JBIG2 Images # @@ -279,7 +280,8 @@ typedef struct _cairo_pdf_alpha_linear_function { } cairo_pdf_alpha_linear_function_t; static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface, + cairo_bool_t clear_doc_surfaces); static void _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); @@ -337,6 +339,9 @@ _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); static cairo_bool_t _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); +static cairo_bool_t +_cairo_pdf_color_glyph_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; @@ -484,12 +489,18 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL0; } + surface->color_glyphs = _cairo_hash_table_create (_cairo_pdf_color_glyph_equal); + if (unlikely (surface->color_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL1; + } + _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 BAIL1; + goto BAIL2; } _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); @@ -498,7 +509,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->pages_resource = _cairo_pdf_surface_new_object (surface); if (surface->pages_resource.id == 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL2; + goto BAIL3; } surface->struct_tree_root.id = 0; @@ -514,11 +525,13 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->object_stream.objects, sizeof (cairo_xref_stream_object_t)); surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + surface->type3_replay = FALSE; surface->force_fallbacks = FALSE; surface->select_pattern_gstate_saved = FALSE; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; + surface->reset_gs_required = FALSE; surface->header_emitted = FALSE; _cairo_surface_clipper_init (&surface->clipper, @@ -536,7 +549,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, status = _cairo_pdf_interchange_init (surface); if (unlikely (status)) - goto BAIL2; + goto BAIL3; surface->page_parent_tree = -1; _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t)); @@ -567,8 +580,10 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, return surface->paginated_surface; } -BAIL2: +BAIL3: _cairo_scaled_font_subsets_destroy (surface->font_subsets); +BAIL2: + _cairo_hash_table_destroy (surface->color_glyphs); BAIL1: _cairo_hash_table_destroy (surface->all_surfaces); BAIL0: @@ -779,7 +794,7 @@ cairo_pdf_get_versions (cairo_pdf_version_t const **versions, const char * cairo_pdf_version_to_string (cairo_pdf_version_t version) { - if (version >= CAIRO_PDF_VERSION_LAST) + if (version < 0 || version >= CAIRO_PDF_VERSION_LAST) return NULL; return _cairo_pdf_version_strings[version]; @@ -997,12 +1012,14 @@ cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface, } static void -_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface, + cairo_bool_t clear_doc_surfaces) { int i, size; cairo_pdf_pattern_t *pattern; cairo_pdf_source_surface_t *src_surface; cairo_pdf_smask_group_t *group; + cairo_pdf_source_surface_t doc_surface; size = _cairo_array_num_elements (&surface->page_patterns); for (i = 0; i < size; i++) { @@ -1014,7 +1031,13 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) size = _cairo_array_num_elements (&surface->page_surfaces); for (i = 0; i < size; i++) { src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i); - cairo_surface_destroy (src_surface->surface); + if (src_surface->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_pattern_destroy (src_surface->raster_pattern); + } else { + if (_cairo_surface_is_recording (src_surface->surface) && src_surface->region_id != 0) + _cairo_recording_surface_region_array_remove (src_surface->surface, src_surface->region_id); + cairo_surface_destroy (src_surface->surface); + } } _cairo_array_truncate (&surface->page_surfaces, 0); @@ -1030,6 +1053,21 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) if (surface->thumbnail_image) cairo_surface_destroy (&surface->thumbnail_image->base); surface->thumbnail_image = NULL; + + if (clear_doc_surfaces) { + size = _cairo_array_num_elements (&surface->doc_surfaces); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface); + if (doc_surface.type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_pattern_destroy (doc_surface.raster_pattern); + } else { + if (_cairo_surface_is_recording (doc_surface.surface) && doc_surface.region_id != 0) + _cairo_recording_surface_region_array_remove (doc_surface.surface, doc_surface.region_id); + cairo_surface_destroy (doc_surface.surface); + } + } + _cairo_array_truncate (&surface->doc_surfaces, 0); + } } static void @@ -1436,6 +1474,25 @@ _cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) } } +static cairo_bool_t +_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_color_glyph_t *a = key_a; + const cairo_pdf_color_glyph_t *b = key_b; + + if (a->scaled_font != b->scaled_font) + return FALSE; + + return (a->glyph_index == b->glyph_index); +} + +static void +_cairo_pdf_color_glyph_init_key (cairo_pdf_color_glyph_t *key) +{ + key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE, (uintptr_t)key->scaled_font); + key->base.hash = _cairo_hash_uintptr (key->base.hash, key->glyph_index); +} + static cairo_int_status_t _cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, @@ -1723,6 +1780,7 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, _cairo_pdf_source_surface_init_key (surface_entry); src_surface.hash_entry = surface_entry; + src_surface.region_id = 0; if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE; src_surface.surface = NULL; @@ -1734,6 +1792,16 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, src_surface.type = CAIRO_PATTERN_TYPE_SURFACE; src_surface.surface = cairo_surface_reference (source_surface); src_surface.raster_pattern = NULL; + if (source_pattern) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern; + src_surface.region_id = surface_pattern->region_array_id; + if (_cairo_surface_is_recording (surface_pattern->surface) && + surface_pattern->region_array_id != 0) + { + _cairo_recording_surface_region_array_reference (surface_pattern->surface, + surface_pattern->region_array_id); + } + } } surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); @@ -2113,7 +2181,7 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, surface->group_stream.bbox = *bbox; /* Reset gstate */ - _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->reset_gs_required = TRUE; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); @@ -2470,15 +2538,26 @@ _cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) free (surface_entry); } +static void +_cairo_pdf_color_glyph_pluck (void *entry, void *closure) +{ + cairo_pdf_color_glyph_t *glyph_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &glyph_entry->base); + cairo_scaled_font_destroy (glyph_entry->scaled_font); + + free (glyph_entry); +} + static cairo_status_t _cairo_pdf_surface_finish (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; long long offset; cairo_pdf_resource_t catalog; - cairo_status_t status, status2; + cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; int size, i; - cairo_pdf_source_surface_t doc_surface; cairo_pdf_jbig2_global_t *global; char *label; cairo_pdf_resource_t xref_res; @@ -2487,7 +2566,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) if (surface->base.status != CAIRO_STATUS_SUCCESS) goto CLEANUP; - _cairo_pdf_surface_clear (surface); + _cairo_pdf_surface_clear (surface, FALSE); status = _cairo_pdf_surface_open_object_stream (surface); if (unlikely (status)) @@ -2496,10 +2575,17 @@ _cairo_pdf_surface_finish (void *abstract_surface) /* Emit unbounded surfaces */ _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); + _cairo_pdf_surface_clear (surface, TRUE); + status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS) status = _cairo_pdf_surface_emit_font_subsets (surface); + /* Emit any new patterns or surfaces created by the Type 3 font subset. */ + _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); + + _cairo_pdf_surface_clear (surface, TRUE); + status = _cairo_pdf_surface_write_pages (surface); if (unlikely (status)) return status; @@ -2590,11 +2676,6 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->page_surfaces); _cairo_array_fini (&surface->object_stream.objects); - size = _cairo_array_num_elements (&surface->doc_surfaces); - for (i = 0; i < size; i++) { - _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface); - cairo_surface_destroy (doc_surface.surface); - } _cairo_array_fini (&surface->doc_surfaces); _cairo_hash_table_foreach (surface->all_surfaces, _cairo_pdf_source_surface_entry_pluck, @@ -2606,6 +2687,11 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->page_annots); _cairo_array_fini (&surface->forward_links); + _cairo_hash_table_foreach (surface->color_glyphs, + _cairo_pdf_color_glyph_pluck, + surface->color_glyphs); + _cairo_hash_table_destroy (surface->color_glyphs); + if (surface->font_subsets) { _cairo_scaled_font_subsets_destroy (surface->font_subsets); surface->font_subsets = NULL; @@ -3535,9 +3621,10 @@ _cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; /* ensure params_string is null terminated */ - params = malloc (ccitt_params_string_len + 1); - memcpy (params, ccitt_params_string, ccitt_params_string_len); - params[ccitt_params_string_len] = 0; + params = _cairo_strndup ((const char *)ccitt_params_string, ccitt_params_string_len); + if (unlikely (params == NULL)) + return _cairo_surface_set_error (&surface->base, CAIRO_STATUS_NO_MEMORY); + status = _cairo_tag_parse_ccitt_params (params, &ccitt_params); if (unlikely(status)) return source->status; @@ -3702,7 +3789,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, goto err; /* Reset gstate */ - _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->reset_gs_required = TRUE; if (source->content == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); @@ -3719,6 +3806,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, } status = _cairo_recording_surface_replay_region (source, + pdf_source->region_id, is_subsurface ? extents : NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); @@ -5054,6 +5142,7 @@ _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, alpha != 1.0, /* need_transp_group */ extents, smask_res, + &pdf_source, &x_offset, &y_offset, @@ -5241,6 +5330,11 @@ _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, { cairo_int_status_t status; + if (surface->reset_gs_required) { + _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->reset_gs_required = FALSE; + } + if (op == surface->current_operator) return CAIRO_STATUS_SUCCESS; @@ -5404,7 +5498,7 @@ _cairo_pdf_surface_show_page (void *abstract_surface) if (unlikely (status)) return status; - _cairo_pdf_surface_clear (surface); + _cairo_pdf_surface_clear (surface, FALSE); return CAIRO_STATUS_SUCCESS; } @@ -6454,42 +6548,203 @@ _cairo_pdf_emit_imagemask (cairo_image_surface_t *image, } static cairo_int_status_t -_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; - cairo_int_status_t status2; - unsigned int i; - cairo_surface_t *type3_surface; - cairo_output_stream_t *null_stream; +cairo_pdf_surface_emit_color_glyph (cairo_pdf_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width) +{ + cairo_rectangle_int_t extents; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t mat; + cairo_int_status_t status; + double x_advance, y_advance; + cairo_matrix_t font_matrix_inverse; + cairo_surface_t *analysis; + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; + cairo_paginated_mode_t old_paginated_mode; + cairo_surface_t *glyph_surface = NULL; + unsigned int regions_id = 0; + cairo_surface_pattern_t surface_pattern; + + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (status == CAIRO_INT_STATUS_SUCCESS) + glyph_surface = cairo_surface_reference (scaled_glyph->recording_surface); - null_stream = _cairo_null_stream_create (); - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - null_stream, - _cairo_pdf_emit_imagemask, - surface->font_subsets, - FALSE); - if (unlikely (type3_surface->status)) { - status2 = _cairo_output_stream_destroy (null_stream); - return type3_surface->status; + _cairo_scaled_font_thaw_cache (scaled_font); + if (unlikely (status)) + return status; + + analysis = _cairo_analysis_surface_create (&surface->base, TRUE); + if (unlikely (analysis->status)) { + status = _cairo_surface_set_error (&surface->base, analysis->status); + goto cleanup; } - _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, - _cairo_pdf_surface_add_font, - surface); + extents.x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); + extents.y = _cairo_fixed_to_double (scaled_glyph->bbox.p1.y); + extents.width = _cairo_fixed_to_double (scaled_glyph->bbox.p2.x) - extents.x; + extents.height = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y) - extents.y; - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, - font_subset->glyphs[i]); - if (unlikely (status)) - break; + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; + old_paginated_mode = surface->paginated_mode; + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + surface->type3_replay = TRUE; + surface->surface_extents = extents; + surface->surface_bounded = TRUE; + + status = _cairo_recording_surface_region_array_attach (glyph_surface, ®ions_id); + if (status) + goto cleanup; + + status = _cairo_recording_surface_replay_and_create_regions (glyph_surface, regions_id, + NULL, analysis, TRUE); + if (status) + goto cleanup; + + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; + surface->paginated_mode = old_paginated_mode; + surface->type3_replay = FALSE; + + if (status == CAIRO_INT_STATUS_SUCCESS && + _cairo_analysis_surface_has_unsupported (analysis)) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; } - cairo_surface_destroy (type3_surface); - status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_INT_STATUS_SUCCESS) - status = status2; + cairo_surface_destroy (analysis); + if (status) + goto cleanup; + + _cairo_pattern_init_for_surface (&surface_pattern, glyph_surface); + surface_pattern.region_array_id = regions_id; + + cairo_matrix_init_identity (&mat); + cairo_matrix_multiply (&mat, &mat, &scaled_font->scale_inverse); + + /* transform glyph extents to operation space */ + cairo_box_t box; + _cairo_box_from_rectangle (&box, &extents); + _cairo_matrix_transform_bounding_box_fixed (&mat, &box, NULL); + _cairo_box_round_to_rectangle (&box, &extents); + + status = cairo_matrix_invert (&mat); + if (status) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + cairo_pattern_set_matrix (&surface_pattern.base, &mat); + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + font_matrix_inverse = scaled_font->font_matrix; + status = cairo_matrix_invert (&font_matrix_inverse); + if (status) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); + *width = x_advance; + + *bbox = scaled_glyph->bbox; + _cairo_matrix_transform_bounding_box_fixed (&scaled_font->scale_inverse, + bbox, NULL); + + _cairo_output_stream_printf (surface->output, + "%f 0 d0\n", + x_advance); + + _cairo_pdf_surface_paint_surface_pattern (surface, + CAIRO_OPERATOR_OVER, + &surface_pattern.base, + &extents, + 1.0, /* alpha */ + NULL, /* smask_res */ + FALSE); /* mask */ + + cleanup: + cairo_surface_destroy (glyph_surface); + + return status; +} + +static cairo_int_status_t +cairo_pdf_surface_emit_color_glyph_image (cairo_pdf_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width) +{ + cairo_rectangle_int_t extents; + cairo_pattern_t *image_pattern; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t mat; + cairo_int_status_t status, status2; + double x_advance, y_advance; + cairo_matrix_t font_matrix_inverse; + + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) + goto FAIL; + + extents.x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); + extents.y = _cairo_fixed_to_double (scaled_glyph->bbox.p1.y); + extents.width = _cairo_fixed_to_double (scaled_glyph->bbox.p2.x) - extents.x; + extents.height = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y) - extents.y; + + image_pattern = cairo_pattern_create_for_surface (&scaled_glyph->color_surface->base); + + cairo_matrix_init_translate (&mat, extents.x, extents.y); + cairo_matrix_multiply (&mat, &mat, &scaled_font->scale_inverse); + status2 = cairo_matrix_invert (&mat); + cairo_pattern_set_matrix (image_pattern, &mat); + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + font_matrix_inverse = scaled_font->font_matrix; + status2 = cairo_matrix_invert (&font_matrix_inverse); + + /* The invertability of font_matrix is tested in + * pdf_operators_show_glyphs before any glyphs are mapped to the + * subset. */ + assert (status2 == CAIRO_INT_STATUS_SUCCESS); + + cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); + *width = x_advance; + + *bbox = scaled_glyph->bbox; + _cairo_matrix_transform_bounding_box_fixed (&scaled_font->scale_inverse, + bbox, NULL); + + _cairo_output_stream_printf (surface->output, + "%f 0 d0\n", + x_advance); + + _cairo_pdf_surface_paint_surface_pattern (surface, + CAIRO_OPERATOR_OVER, + image_pattern, + &extents, + 1.0, /* alpha */ + NULL, /* smask_res */ + FALSE); /* mask */ + cairo_pattern_destroy (image_pattern); + FAIL: + _cairo_scaled_font_thaw_cache (scaled_font); return status; } @@ -6556,6 +6811,20 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, font_subset->glyphs[i], &bbox, &widths[i]); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = cairo_pdf_surface_emit_color_glyph (surface, + font_subset->scaled_font, + font_subset->glyphs[i], + &bbox, + &widths[i]); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = cairo_pdf_surface_emit_color_glyph_image (surface, + font_subset->scaled_font, + font_subset->glyphs[i], + &bbox, + &widths[i]); + } + } if (unlikely (status)) break; @@ -6731,12 +7000,6 @@ _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) { cairo_int_status_t status; - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_analyze_user_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, _cairo_pdf_surface_emit_unscaled_font_subset, surface); @@ -6746,12 +7009,6 @@ _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, _cairo_pdf_surface_emit_scaled_font_subset, surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_emit_scaled_font_subset, - surface); BAIL: _cairo_scaled_font_subsets_destroy (surface->font_subsets); @@ -7630,7 +7887,7 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) { if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x || _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y || - _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width || + _cairo_fixed_integer_floor(box.p2.x) > rec_extents.x + rec_extents.width || _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height) { return CAIRO_INT_STATUS_UNSUPPORTED; @@ -7648,6 +7905,9 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, /* The SOURCE operator is supported if the pattern is opaque or if * there is nothing painted underneath. */ if (op == CAIRO_OPERATOR_SOURCE) { + if (surface->type3_replay) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; @@ -8783,6 +9043,13 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + /* Enabling text in Type 3 fonts currently crashes cairo. Most + * PDF viewers don't seem to suport text in Type 3 so we let + * this go to image fallback. + */ + if (surface->type3_replay) + return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } @@ -8936,6 +9203,68 @@ _cairo_pdf_surface_tag (void *abstract_surface, return status; } +/* The Type 3 font subset support will the embed the + * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE image if vector operations + * are not supported. The only case we don't currently handle is if a + * foreground color is used. + */ +static cairo_bool_t +_cairo_pdf_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_color_glyph_t glyph_key; + cairo_pdf_color_glyph_t *glyph_entry; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + glyph_key.scaled_font = scaled_font; + glyph_key.glyph_index = glyph_index; + + _cairo_pdf_color_glyph_init_key (&glyph_key); + glyph_entry = _cairo_hash_table_lookup (surface->color_glyphs, &glyph_key.base); + if (glyph_entry) + return glyph_entry->supported; + + glyph_entry = _cairo_malloc (sizeof (cairo_pdf_color_glyph_t)); + if (glyph_entry == NULL) { + status = _cairo_surface_set_error (&surface->base, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + return FALSE; + } + + glyph_entry->scaled_font = cairo_scaled_font_reference (scaled_font); + glyph_entry->glyph_index = glyph_index; + _cairo_pdf_color_glyph_init_key (glyph_entry); + + glyph_entry->supported = FALSE; + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + if (unlikely (status)) + goto done; + + glyph_entry->supported = !(scaled_glyph->recording_uses_foreground_color || + scaled_glyph->recording_uses_foreground_marker); + + done: + _cairo_scaled_font_thaw_cache (scaled_font); + + status = _cairo_hash_table_insert (surface->color_glyphs, + &glyph_entry->base); + if (unlikely(status)) { + status = _cairo_surface_set_error (&surface->base, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + return FALSE; + } + + return glyph_entry->supported; +} + static cairo_int_status_t _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) @@ -8994,6 +9323,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_show_text_glyphs, _cairo_pdf_surface_get_supported_mime_types, _cairo_pdf_surface_tag, + _cairo_pdf_surface_supports_color_glyph, }; static const cairo_paginated_surface_backend_t diff --git a/src/cairo-png.c b/src/cairo-png.c index 4b7c34081..5b9c58447 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -987,3 +987,4 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, return read_png (&png_closure); } +slim_hidden_def (cairo_image_surface_create_from_png_stream); diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h index f18403190..e825b57fd 100644 --- a/src/cairo-ps-surface-private.h +++ b/src/cairo-ps-surface-private.h @@ -56,6 +56,7 @@ typedef struct _cairo_ps_form { cairo_bool_t is_image; int id; cairo_surface_t *src_surface; + unsigned int regions_id; cairo_rectangle_int_t src_surface_extents; cairo_bool_t src_surface_bounded; cairo_filter_t filter; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 5645aae4a..381b4cf75 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -66,18 +66,19 @@ #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-image-info-private.h" #include "cairo-image-surface-inline.h" #include "cairo-list-inline.h" -#include "cairo-scaled-font-subsets-private.h" +#include "cairo-output-stream-private.h" #include "cairo-paginated-private.h" +#include "cairo-recording-surface-inline.h" #include "cairo-recording-surface-private.h" +#include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-type3-glyph-surface-private.h" -#include "cairo-image-info-private.h" #include "cairo-tag-attributes-private.h" +#include "cairo-type3-glyph-surface-private.h" #include <stdio.h> #include <ctype.h> @@ -118,9 +119,8 @@ static char *ctime_r(const time_t *timep, char *buf) * The PostScript surface is used to render cairo graphics to Adobe * PostScript files and is a multi-page vector surface backend. * - * The following mime types are supported: %CAIRO_MIME_TYPE_JPEG, - * %CAIRO_MIME_TYPE_UNIQUE_ID, - * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + * The following mime types are supported on source patterns: + * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_UNIQUE_ID, * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, * %CAIRO_MIME_TYPE_EPS, %CAIRO_MIME_TYPE_EPS_PARAMS. * @@ -148,7 +148,7 @@ static char *ctime_r(const time_t *timep, char *buf) * ury]" that specifies the bounding box (in PS coordinates) of the * EPS graphics. The parameters are: lower left x, lower left y, upper * right x, upper right y. Normally the bbox data is identical to the - * %%%BoundingBox data in the EPS file. + * \%\%\%BoundingBox data in the EPS file. * **/ @@ -176,6 +176,7 @@ typedef enum { typedef struct { /* input params */ cairo_surface_t *src_surface; + unsigned int regions_id; cairo_operator_t op; const cairo_rectangle_int_t *src_surface_extents; cairo_bool_t src_surface_bounded; @@ -217,8 +218,11 @@ static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = static const char *_cairo_ps_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_UNIQUE_ID, CAIRO_MIME_TYPE_CCITT_FAX, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + CAIRO_MIME_TYPE_EPS, + CAIRO_MIME_TYPE_EPS_PARAMS, NULL }; @@ -286,6 +290,8 @@ _cairo_ps_form_pluck (void *entry, void *closure) _cairo_hash_table_remove (patterns, &surface_entry->base); free (surface_entry->unique_id); + if (_cairo_surface_is_recording (surface_entry->src_surface) && surface_entry->regions_id != 0) + _cairo_recording_surface_region_array_remove (surface_entry->src_surface, surface_entry->regions_id); cairo_surface_destroy (surface_entry->src_surface); free (surface_entry); } @@ -734,34 +740,6 @@ _cairo_ps_emit_imagemask (cairo_image_surface_t *image, return _cairo_output_stream_get_status (stream); } -static cairo_int_status_t -_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_ps_surface_t *surface = closure; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - unsigned int i; - cairo_surface_t *type3_surface; - - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - NULL, - _cairo_ps_emit_imagemask, - surface->font_subsets, - TRUE); - - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, - font_subset->glyphs[i]); - if (unlikely (status)) - break; - - } - cairo_surface_finish (type3_surface); - cairo_surface_destroy (type3_surface); - - return status; -} - static cairo_status_t _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, cairo_scaled_font_subset_t *font_subset) @@ -928,30 +906,17 @@ _cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) "%% _cairo_ps_surface_emit_font_subsets\n"); #endif - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_ps_surface_analyze_user_font_subset, - surface); - if (unlikely (status)) - return status; - status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, _cairo_ps_surface_emit_unscaled_font_subset, surface); if (unlikely (status)) return status; - status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, - _cairo_ps_surface_emit_scaled_font_subset, - surface); - if (unlikely (status)) - return status; - - return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_ps_surface_emit_scaled_font_subset, - surface); + return _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, + _cairo_ps_surface_emit_scaled_font_subset, + surface); } - static cairo_int_status_t _cairo_ps_surface_emit_forms (cairo_ps_surface_t *surface) { @@ -3095,9 +3060,10 @@ _cairo_ps_surface_emit_ccitt_image (cairo_ps_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; /* ensure params_string is null terminated */ - ccitt_params_string = malloc (ccitt_params_data_len + 1); - memcpy (ccitt_params_string, ccitt_params_data, ccitt_params_data_len); - ccitt_params_string[ccitt_params_data_len] = 0; + ccitt_params_string = _cairo_strndup ((const char *)ccitt_params_data, ccitt_params_data_len); + if (unlikely (ccitt_params_string == NULL)) + return _cairo_surface_set_error (&surface->base, CAIRO_STATUS_NO_MEMORY); + status = _cairo_tag_parse_ccitt_params (ccitt_params_string, &ccitt_params); if (unlikely(status)) return status; @@ -3280,9 +3246,10 @@ _cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; /* ensure params_string is null terminated */ - params_string = malloc (eps_params_string_len + 1); - memcpy (params_string, eps_params_string, eps_params_string_len); - params_string[eps_params_string_len] = 0; + params_string = _cairo_strndup ((const char *)eps_params_string, eps_params_string_len); + if (unlikely (params_string == NULL)) + return _cairo_surface_set_error (&surface->base, CAIRO_STATUS_NO_MEMORY); + status = _cairo_tag_parse_eps_params (params_string, &eps_params); if (unlikely(status)) return status; @@ -3327,7 +3294,11 @@ _cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface, eps_width, eps_height); + _cairo_output_stream_printf (surface->stream, + "%%%%BeginDocument: Document%d\n", + params->src_surface->unique_id); _cairo_output_stream_write (surface->stream, eps_data, eps_data_len); + _cairo_output_stream_printf (surface->stream, "%%%%EndDocument"); _cairo_output_stream_printf (surface->stream, "\ncairo_eps_end\n"); return CAIRO_STATUS_SUCCESS; @@ -3336,6 +3307,7 @@ _cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface, static cairo_status_t _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, cairo_surface_t *recording_surface, + unsigned int regions_id, const cairo_rectangle_int_t *recording_extents, cairo_bool_t subsurface) { @@ -3406,6 +3378,7 @@ _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, } status = _cairo_recording_surface_replay_region (recording_surface, + regions_id, subsurface ? recording_extents : NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); @@ -3558,6 +3531,9 @@ _cairo_ps_surface_use_form (cairo_ps_surface_t *surface, source_entry->unique_id = unique_id; source_entry->id = surface->num_forms++; source_entry->src_surface = cairo_surface_reference (params->src_surface); + source_entry->regions_id = params->regions_id; + if (_cairo_surface_is_recording (source_entry->src_surface) && source_entry->regions_id != 0) + _cairo_recording_surface_region_array_reference (source_entry->src_surface, source_entry->regions_id); source_entry->src_surface_extents = *params->src_surface_extents; source_entry->src_surface_bounded = params->src_surface_bounded; source_entry->required_extents = *params->src_op_extents; @@ -3690,11 +3666,13 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) params->src_surface; status = _cairo_ps_surface_emit_recording_surface (surface, sub->target, + params->regions_id, &sub->extents, TRUE); } else { status = _cairo_ps_surface_emit_recording_surface (surface, params->src_surface, + params->regions_id, params->src_op_extents, FALSE); } @@ -3713,11 +3691,11 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, status = _cairo_memory_stream_destroy (surface->stream, &data, &length); free (data); + surface->stream = old_stream; if (unlikely (status)) return status; params->approx_size = length; - surface->stream = old_stream; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); } @@ -3737,6 +3715,7 @@ _cairo_ps_form_emit (void *entry, void *closure) cairo_output_stream_t *old_stream; params.src_surface = form->src_surface; + params.regions_id = form->regions_id; params.op = CAIRO_OPERATOR_OVER; params.src_surface_extents = &form->src_surface_extents; params.src_surface_bounded = form->src_surface_bounded; @@ -3878,11 +3857,17 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, cairo_path_fixed_t path; cairo_emit_surface_params_t params; cairo_image_surface_t *image = NULL; + unsigned int region_id = 0; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + region_id = surface_pattern->region_array_id; + } + status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, @@ -3968,6 +3953,7 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, cairo_matrix_translate (&ps_p2d, x_offset, y_offset); params.src_surface = image ? &image->base : source_surface; + params.regions_id = image ? 0 : region_id; params.op = op; params.src_surface_extents = &src_surface_extents; params.src_surface_bounded = src_surface_bounded; @@ -4022,12 +4008,18 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, cairo_rectangle_int_t src_op_extents; cairo_emit_surface_params_t params; cairo_extend_t extend = cairo_pattern_get_extend (pattern); + unsigned int region_id = 0; cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + region_id = surface_pattern->region_array_id; + } + status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, @@ -4121,6 +4113,7 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, old_paint_proc = surface->paint_proc; surface->paint_proc = TRUE; params.src_surface = image ? &image->base : source_surface; + params.regions_id = image ? 0 : region_id; params.op = op; params.src_surface_extents = &pattern_extents; params.src_surface_bounded = bounded; diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 641a2dfc7..1e7531356 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -656,7 +656,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, cairo_surface_mark_dirty (&surface->base); if (is_color) - _cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color != NULL); + _cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color); else _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c index 30d92d6be..d09f5b5bc 100644 --- a/src/cairo-quartz-image-surface.c +++ b/src/cairo-quartz-image-surface.c @@ -49,12 +49,6 @@ #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 -DataProviderReleaseCallback (void *image_info, const void *data, size_t size) -{ - free (image_info); -} - static cairo_surface_t * _cairo_quartz_image_surface_create_similar (void *asurface, cairo_content_t content, @@ -87,8 +81,9 @@ _cairo_quartz_image_surface_finish (void *asurface) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - CGImageRelease (surface->image); - cairo_surface_destroy ( (cairo_surface_t*) surface->imageSurface); + CGContextRelease (surface->cgContext); + if (surface->imageSurface) + cairo_surface_destroy ( (cairo_surface_t*) surface->imageSurface); return CAIRO_STATUS_SUCCESS; } @@ -134,47 +129,6 @@ _cairo_quartz_image_surface_get_extents (void *asurface, return TRUE; } -/* we assume some drawing happened to the image buffer; make sure it's - * represented in the CGImage on flush() - */ - -static cairo_status_t -_cairo_quartz_image_surface_flush (void *asurface, - unsigned flags) -{ - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - CGImageRef oldImage = surface->image; - CGImageRef newImage = NULL; - void *image_data; - const unsigned int size = surface->imageSurface->height * surface->imageSurface->stride; - if (flags) - return CAIRO_STATUS_SUCCESS; - - /* XXX only flush if the image has been modified. */ - - image_data = _cairo_malloc_ab ( surface->imageSurface->height, - surface->imageSurface->stride); - if (unlikely (!image_data)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (image_data, surface->imageSurface->data, - surface->imageSurface->height * surface->imageSurface->stride); - newImage = CairoQuartzCreateCGImage (surface->imageSurface->format, - surface->imageSurface->width, - surface->imageSurface->height, - surface->imageSurface->stride, - image_data, - TRUE, - NULL, - DataProviderReleaseCallback, - image_data); - - surface->image = newImage; - CGImageRelease (oldImage); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_int_status_t _cairo_quartz_image_surface_paint (void *abstract_surface, cairo_operator_t op, @@ -275,7 +229,7 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { _cairo_quartz_image_surface_get_extents, NULL, /* get_font_options */ - _cairo_quartz_image_surface_flush, + NULL, /*surface_flush */ NULL, /* mark_dirty_rectangle */ _cairo_quartz_image_surface_paint, @@ -290,12 +244,9 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { * cairo_quartz_image_surface_create: * @image_surface: a cairo image surface to wrap with a quartz image surface * - * Creates a Quartz surface backed by a CGImageRef that references the + * Creates a Quartz surface backed by a CGBitmapContext that references the * given image surface. The resulting surface can be rendered quickly - * when used as a source when rendering to a #cairo_quartz_surface. If - * the data in the image surface is ever updated, cairo_surface_flush() - * must be called on the #cairo_quartz_image_surface to ensure that the - * CGImageRef refers to the updated data. + * when used as a source when rendering to a #cairo_quartz_surface. * * Return value: the newly created surface. * @@ -305,13 +256,11 @@ cairo_surface_t * cairo_quartz_image_surface_create (cairo_surface_t *surface) { cairo_quartz_image_surface_t *qisurf; - - CGImageRef image; - cairo_image_surface_t *image_surface; int width, height, stride; cairo_format_t format; - void *image_data; + CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host; + CGColorSpaceRef colorspace; if (surface->status) return surface; @@ -338,54 +287,100 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) if (qisurf == NULL) return SURFACE_ERROR_NO_MEMORY; - memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t)); - - image_data = _cairo_malloc_ab (height, stride); - if (unlikely (!image_data)) { - free(qisurf); - return SURFACE_ERROR_NO_MEMORY; - } - - memcpy (image_data, image_surface->data, height * stride); - image = CairoQuartzCreateCGImage (format, - width, height, - stride, - image_data, - TRUE, - NULL, - DataProviderReleaseCallback, - image_data); - - if (!image) { - free (qisurf); - return SURFACE_ERROR_NO_MEMORY; - } - _cairo_surface_init (&qisurf->base, &cairo_quartz_image_surface_backend, NULL, /* device */ _cairo_content_from_format (format), FALSE); /* is_vector */ + if (_cairo_surface_is_quartz (surface) || _cairo_surface_is_quartz_image (surface)) { + CGContextRef context = cairo_quartz_surface_get_cg_context(surface); + colorspace = _cairo_quartz_create_color_space (context); + } + else { + colorspace = CGDisplayCopyColorSpace (CGMainDisplayID ()); + } + + bitinfo |= format == CAIRO_FORMAT_ARGB32 ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; + qisurf->width = width; qisurf->height = height; - qisurf->image = image; + qisurf->cgContext = CGBitmapContextCreate (image_surface->data, width, height, 8, image_surface->stride, + colorspace, bitinfo); qisurf->imageSurface = (cairo_image_surface_t*) cairo_surface_reference(surface); + CGColorSpaceRelease (colorspace); return &qisurf->base; } +/** + * cairo_quartz_image_surface_get_image: + * @surface: a #cairo_surface_t + * + * Returns a #cairo_surface_t image surface that refers to the same bits + * as the image of the quartz surface. + * + * Return value: a #cairo_surface_t (owned by the quartz #cairo_surface_t), + * or %NULL if the quartz surface is not an image surface. + * + * Since: 1.6 + */ cairo_surface_t * -cairo_quartz_image_surface_get_image (cairo_surface_t *asurface) +cairo_quartz_image_surface_get_image (cairo_surface_t *surface) { - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface; + cairo_quartz_image_surface_t *qsurface = (cairo_quartz_image_surface_t*) surface; /* Throw an error for a non-quartz surface */ - if (! _cairo_surface_is_quartz (asurface)) { + if (! _cairo_surface_is_quartz (surface)) { return SURFACE_ERROR_TYPE_MISMATCH; } - return (cairo_surface_t*) surface->imageSurface; + return (cairo_surface_t*) qsurface->imageSurface; +} + +/* + * _cairo_quartz_image_surface_get_cg_context: + * @surface: the Cairo Quartz surface + * + * Returns the CGContextRef that the given Quartz surface is backed + * by. + * + * A call to cairo_surface_flush() is required before using the + * CGContextRef to ensure that all pending drawing operations are + * finished and to restore any temporary modification cairo has made + * to its state. A call to cairo_surface_mark_dirty() is required + * after the state or the content of the CGContextRef has been + * modified. + * + * Return value: the CGContextRef for the given surface. + * + **/ +CGContextRef +_cairo_quartz_image_surface_get_cg_context (cairo_surface_t *surface) +{ + if (surface && _cairo_surface_is_quartz_image (surface)) { + cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface; + return quartz->cgContext; + } else + return NULL; +} + +/* + * _cairo_surface_is_quartz_image: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_quartz_surface_t + * + * Return value: True if the surface is an quartz surface + **/ +cairo_bool_t +_cairo_surface_is_quartz_image (const cairo_surface_t *surface) { + return surface->backend == &cairo_quartz_image_surface_backend; +} + +cairo_bool_t +_cairo_quartz_image_surface_is_zero (const cairo_quartz_image_surface_t *surface) { + return surface->width == 0 || surface->height == 0; } diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index f142f8471..bdfdf8c2b 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -81,8 +81,7 @@ typedef struct cairo_quartz_image_surface { cairo_surface_t base; int width, height; - - CGImageRef image; + CGContextRef cgContext; cairo_image_surface_t *imageSurface; } cairo_quartz_image_surface_t; @@ -92,16 +91,15 @@ _cairo_quartz_verify_surface_size(int width, int height); cairo_private cairo_bool_t _cairo_surface_is_quartz (const cairo_surface_t *surface); -cairo_private CGImageRef -CairoQuartzCreateCGImage (cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride, - void *data, - cairo_bool_t interpolate, - CGColorSpaceRef colorSpaceOverride, - CGDataProviderReleaseDataCallback releaseCallback, - void *releaseInfo); +cairo_private cairo_bool_t +_cairo_surface_is_quartz_image (const cairo_surface_t *surface); +cairo_private cairo_bool_t +_cairo_quartz_image_surface_is_zero (const cairo_quartz_image_surface_t *surface); + +cairo_private CGColorSpaceRef +_cairo_quartz_create_color_space (CGContextRef context); +cairo_private CGContextRef +_cairo_quartz_image_surface_get_cg_context (cairo_surface_t *surface); cairo_private CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); @@ -112,6 +110,9 @@ _cairo_quartz_font_face_create_for_ctfont (CTFontRef ctFont); cairo_private void _cairo_quartz_set_antialiasing (CGContextRef context, cairo_antialias_t antialias); +cairo_status_t _cairo_quartz_surface_to_png (cairo_surface_t *abstract_surface, const char *dest); +cairo_private void _cairo_quartz_image_to_png (CGImageRef, const char *dest); + #else # error Cairo was not compiled with support for the quartz backend diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 6676dc960..d8a8065d0 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -37,6 +37,7 @@ #include "cairoint.h" #include "cairo-quartz-private.h" +#include "cairo-quartz-image.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" @@ -55,6 +56,7 @@ #endif #include <limits.h> +#include <assert.h> #undef QUARTZ_DEBUG @@ -64,13 +66,30 @@ #define ND(_x) do {} while(0) #endif -#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) #if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 #define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation #else #define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal #endif +static inline cairo_bool_t +_is_quartz_surface (cairo_surface_t *surface) { + return _cairo_surface_is_quartz (surface) || _cairo_surface_is_quartz_image (surface); +} + +static inline cairo_bool_t +_cairo_quartz_surface_is_zero (cairo_quartz_surface_t* surface) { + return surface->extents.width == 0 || surface->extents.height == 0; +} + +static inline cairo_bool_t +_cairo_quartz_is_zero_surface (cairo_surface_t* surface) { + assert (_is_quartz_surface (surface)); + if (_cairo_surface_is_quartz (surface)) + return (_cairo_quartz_surface_is_zero ((cairo_quartz_surface_t*) surface)); + else + return (_cairo_quartz_image_surface_is_zero ((cairo_quartz_image_surface_t*) surface)); +} /** * SECTION:cairo-quartz @@ -94,6 +113,20 @@ /* * macOS Private functions */ +typedef enum { + kCGContextTypeUnknown, + kCGContextTypePDF, + kCGContextTypePostScript, + kCGContextTypeWindow, + kCGContextTypeBitmap, + kCGContextTypeGL, + kCGContextTypeDisplayList, + kCGContextTypeKSeparation, + kCGContextTypeIOSurface, + kCGContextTypeCount +} CGContextType; + + static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; static void @@ -108,20 +141,15 @@ quartz_ensure_symbols() } } -#ifdef QUARTZ_DEBUG -static void quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest); -static void quartz_image_to_png (CGImageRef, const char *dest); -#endif - typedef struct { cairo_surface_t base; CGImageRef image; } cairo_quartz_snapshot_t; -static cairo_surface_t* _cairo_quartz_snapshot_create (cairo_quartz_surface_t *surface); -static cairo_status_t _cairo_quartz_snapshot_finish (void* surface); -static CGImageRef _cairo_quartz_surface_snapshot_get_image (cairo_quartz_surface_t *surface); +static cairo_surface_t* _cairo_quartz_snapshot_create (cairo_surface_t *surface); +static cairo_status_t _cairo_quartz_snapshot_finish (void *surface); +static CGImageRef _cairo_quartz_surface_snapshot_get_image (cairo_surface_t *surface); static const cairo_surface_backend_t cairo_quartz_snapshot_backend = { CAIRO_INTERNAL_SURFACE_TYPE_QUARTZ_SNAPSHOT, @@ -134,102 +162,72 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, unsigned int width, unsigned int height); -CGImageRef -CairoQuartzCreateCGImage (cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride, - void *data, - cairo_bool_t interpolate, - CGColorSpaceRef colorSpaceOverride, - CGDataProviderReleaseDataCallback releaseCallback, - void *releaseInfo) +CGColorSpaceRef +_cairo_quartz_create_color_space (CGContextRef context) { - CGImageRef image = NULL; - CGDataProviderRef dataProvider = NULL; - CGColorSpaceRef colorSpace = colorSpaceOverride; - CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host; - int bitsPerComponent, bitsPerPixel; - - switch (format) { - case CAIRO_FORMAT_ARGB32: - if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceRGB (); - bitinfo |= kCGImageAlphaPremultipliedFirst; - bitsPerComponent = 8; - bitsPerPixel = 32; - break; - - case CAIRO_FORMAT_RGB24: - if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceRGB (); - bitinfo |= kCGImageAlphaNoneSkipFirst; - bitsPerComponent = 8; - bitsPerPixel = 32; - break; - - case CAIRO_FORMAT_A8: - bitsPerComponent = 8; - bitsPerPixel = 8; - break; - - case CAIRO_FORMAT_A1: -#ifdef WORDS_BIGENDIAN - bitsPerComponent = 1; - bitsPerPixel = 1; - break; -#endif - - case CAIRO_FORMAT_RGB30: - case CAIRO_FORMAT_RGB16_565: - case CAIRO_FORMAT_RGB96F: - case CAIRO_FORMAT_RGBA128F: - case CAIRO_FORMAT_INVALID: - default: - return NULL; - } - - dataProvider = CGDataProviderCreateWithData (releaseInfo, - data, - height * stride, - releaseCallback); + CGColorSpaceRef color_space = NULL; + CGContextType cgtype = kCGContextTypeUnknown; - if (unlikely (!dataProvider)) { - // manually release - if (releaseCallback) - releaseCallback (releaseInfo, data, height * stride); - goto FINISH; + if (context) + { + if (CGBitmapContextGetBitsPerPixel (context) < 24) + return 0; + + quartz_ensure_symbols(); + cgtype = CGContextGetTypePtr (context); + switch (cgtype) + { + case kCGContextTypeUnknown: + break; + case kCGContextTypePDF: + color_space = CGColorSpaceCreateDeviceRGB (); + break; + case kCGContextTypePostScript: + case kCGContextTypeWindow: + break; + case kCGContextTypeBitmap: + color_space = CGBitmapContextGetColorSpace (context); + color_space = CGColorSpaceRetain (color_space); + break; + case kCGContextTypeGL: + case kCGContextTypeDisplayList: + case kCGContextTypeKSeparation: + case kCGContextTypeIOSurface: + case kCGContextTypeCount: + default: + break; + } + if (color_space) + return color_space; } + if (!color_space) + color_space = CGDisplayCopyColorSpace (CGMainDisplayID ()); - if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) { - cairo_quartz_float_t decode[] = {1.0, 0.0}; - image = CGImageMaskCreate (width, height, - bitsPerComponent, - bitsPerPixel, - stride, - dataProvider, - decode, - interpolate); - } else - image = CGImageCreate (width, height, - bitsPerComponent, - bitsPerPixel, - stride, - colorSpace, - bitinfo, - dataProvider, - NULL, - interpolate, - kCGRenderingIntentDefault); - -FINISH: + if (!color_space) + color_space = CGColorSpaceCreateDeviceRGB (); - CGDataProviderRelease (dataProvider); + return color_space; +} - if (colorSpace != colorSpaceOverride) - CGColorSpaceRelease (colorSpace); +static CGColorRef +_cairo_quartz_create_cgcolor (CGColorSpaceRef cs, CGFloat red, CGFloat green, + CGFloat blue, CGFloat alpha) +{ + CGFloat colors[4] = { red, green, blue, alpha }; + CGColorRef cgc; + if (!CGColorSpaceRetain(cs)) + { + cs = _cairo_quartz_create_color_space (NULL); + } + cgc = CGColorCreate (cs, colors); + CGColorSpaceRelease (cs); + return cgc; +} - return image; +static CGColorRef +_cairo_quartz_black (CGColorSpaceRef cs) +{ + return _cairo_quartz_create_cgcolor (cs, 0.0, 0.0, 0.0, 1.0); } static inline cairo_bool_t @@ -241,7 +239,7 @@ _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) quartz_ensure_symbols (); if (likely (CGContextGetTypePtr)) { /* 4 is the type value of a bitmap context */ - return CGContextGetTypePtr (cgc) == 4; + return CGContextGetTypePtr (cgc) == kCGContextTypeBitmap; } /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */ @@ -682,12 +680,6 @@ CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient, &gradient_callbacks); } -static void -DataProviderReleaseCallback (void *info, const void *data, size_t size) -{ - free (info); -} - static cairo_status_t _cairo_surface_to_cgimage (cairo_surface_t *source, cairo_rectangle_int_t *extents, @@ -697,25 +689,19 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, CGImageRef *image_out) { cairo_status_t status; - cairo_image_surface_t *image_surface; - void *image_data, *image_extra; + cairo_quartz_image_surface_t *image_surface; + void *image_extra; cairo_bool_t acquired = FALSE; - if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) { - cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source; - *image_out = CGImageRetain (surface->image); - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_surface_is_quartz (source)) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; - if (IS_EMPTY (surface)) { + if (_is_quartz_surface (source)) { + CGContextRef cgContext = cairo_quartz_surface_get_cg_context(source); + if (_cairo_quartz_is_zero_surface (source)) { *image_out = NULL; return CAIRO_INT_STATUS_NOTHING_TO_DO; } - if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { - *image_out = _cairo_quartz_surface_snapshot_get_image (surface); + if (_cairo_quartz_is_cgcontext_bitmap_context (cgContext)) { + *image_out = _cairo_quartz_surface_snapshot_get_image (source); return CAIRO_STATUS_SUCCESS; } @@ -724,79 +710,60 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, } if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { - image_surface = (cairo_image_surface_t *) - cairo_image_surface_create (format, extents->width, extents->height); - if (unlikely (image_surface->base.status)) { - status = image_surface->base.status; - cairo_surface_destroy (&image_surface->base); + cairo_image_surface_t *surface = + (cairo_image_surface_t*)cairo_image_surface_create (format, extents->width, + extents->height); + if (unlikely (surface->base.status)) { + status = surface->base.status; + cairo_surface_destroy (&surface->base); return status; } status = _cairo_recording_surface_replay_with_clip (source, matrix, - &image_surface->base, - NULL); + &surface->base, + NULL, + FALSE); if (unlikely (status)) { - cairo_surface_destroy (&image_surface->base); + cairo_surface_destroy (&surface->base); return status; } + image_surface = + (cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base); cairo_matrix_init_identity (matrix); } else { - status = _cairo_surface_acquire_source_image (source, &image_surface, + cairo_image_surface_t *surface; + status = _cairo_surface_acquire_source_image (source, &surface, &image_extra); if (unlikely (status)) return status; - acquired = TRUE; - } - - if (image_surface->width == 0 || image_surface->height == 0) { - *image_out = NULL; - if (acquired) - _cairo_surface_release_source_image (source, image_surface, image_extra); + image_surface = + (cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base); + status = image_surface->base.status; + if (status) + _cairo_surface_release_source_image (source, surface, image_extra); else - cairo_surface_destroy (&image_surface->base); - - return status; + acquired = TRUE; } - image_data = _cairo_malloc_ab (image_surface->height, image_surface->stride); - if (unlikely (!image_data)) - { - if (acquired) - _cairo_surface_release_source_image (source, image_surface, image_extra); - else - cairo_surface_destroy (&image_surface->base); - - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + *image_out = NULL; + if (image_surface->width > 0 && image_surface->height > 0) { + *image_out = _cairo_quartz_surface_snapshot_get_image (&image_surface->base); + status = CAIRO_STATUS_SUCCESS; } - // The last row of data may have less than stride bytes so make sure we - // only copy the minimum amount required from that row. - memcpy (image_data, image_surface->data, - (image_surface->height - 1) * image_surface->stride + - cairo_format_stride_for_width (image_surface->format, - image_surface->width)); - *image_out = CairoQuartzCreateCGImage (image_surface->format, - image_surface->width, - image_surface->height, - image_surface->stride, - image_data, - TRUE, - NULL, - DataProviderReleaseCallback, - image_data); + if (acquired) { + _cairo_surface_release_source_image (source, image_surface->imageSurface, image_extra); + image_surface->imageSurface = NULL; + } + cairo_surface_destroy (&image_surface->base); /* TODO: differentiate memory error and unsupported surface type */ if (unlikely (*image_out == NULL)) status = CAIRO_INT_STATUS_UNSUPPORTED; - if (acquired) - _cairo_surface_release_source_image (source, image_surface, image_extra); - else - cairo_surface_destroy (&image_surface->base); - return status; } @@ -901,7 +868,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t switch (spattern->base.extend) { case CAIRO_EXTEND_NONE: - break; + case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_REPEAT: pbounds.size.width = extents.width; pbounds.size.height = extents.height; @@ -911,10 +878,6 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t pbounds.size.height = 2.0 * extents.height; info->do_reflect = TRUE; break; - case CAIRO_EXTEND_PAD: - pbounds.size.width = extents.width; - pbounds.size.height = extents.height; - break; } rw = pbounds.size.width; rh = pbounds.size.height; @@ -996,6 +959,7 @@ _cairo_quartz_setup_pattern_source (cairo_quartz_drawing_state_t *state, CGColorSpaceRef patternSpace; CGPatternRef cgpat = NULL; cairo_int_status_t status; + CGColorRef black = _cairo_quartz_black (_cairo_quartz_create_color_space (state->cgDrawContext)); _cairo_surface_get_extents (&surface->base, &extents); @@ -1051,7 +1015,7 @@ _cairo_quartz_setup_pattern_source (cairo_quartz_drawing_state_t *state, -state->clipRect.origin.y); } - CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); + CGContextSetFillColorWithColor (state->cgDrawContext, black); state->rect = CGRectMake (0, 0, pattern_extents.width, pattern_extents.height); state->action = DO_IMAGE; @@ -1090,6 +1054,7 @@ _cairo_quartz_setup_pattern_source (cairo_quartz_drawing_state_t *state, CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0)); CGPatternRelease (cgpat); + CGColorRelease (black); state->action = DO_DIRECT; return CAIRO_STATUS_SUCCESS; @@ -1129,7 +1094,7 @@ _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state, if (unlikely (gradFunc == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; - rgb = CGColorSpaceCreateDeviceRGB (); + rgb = _cairo_quartz_create_color_space (NULL); if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { state->shading = CGShadingCreateAxial (rgb, @@ -1200,9 +1165,11 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, state->filter = _cairo_quartz_filter_to_quartz (source->filter); if (op == CAIRO_OPERATOR_CLEAR) { - CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); + CGColorRef black = _cairo_quartz_black (_cairo_quartz_create_color_space (surface->cgContext)); + CGContextSetFillColorWithColor (state->cgDrawContext, black); state->action = DO_DIRECT; + CGColorRelease (black); return CAIRO_STATUS_SUCCESS; } @@ -1239,18 +1206,17 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + CGColorRef color = _cairo_quartz_create_cgcolor (_cairo_quartz_create_color_space (state->cgDrawContext), + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + - CGContextSetRGBStrokeColor (state->cgDrawContext, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - CGContextSetRGBFillColor (state->cgDrawContext, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); + CGContextSetStrokeColorWithColor (state->cgDrawContext, color); + CGContextSetFillColorWithColor (state->cgDrawContext, color); + CGColorRelease (color); state->action = DO_DIRECT; return CAIRO_STATUS_SUCCESS; } @@ -1304,6 +1270,9 @@ static inline void _cairo_quartz_draw_cgcontext (cairo_quartz_drawing_state_t *state, cairo_operator_t op) { + CGColorSpaceRef cs = _cairo_quartz_create_color_space (state->cgDrawContext); + CGColorRef transparent = _cairo_quartz_create_cgcolor (cs, 0.0, 0.0, 0.0, 0.0); //releases cs + if (! (op == CAIRO_OPERATOR_SOURCE && state->cgDrawContext == state->cgMaskContext)) return; @@ -1318,8 +1287,9 @@ _cairo_quartz_draw_cgcontext (cairo_quartz_drawing_state_t *state, CGContextAddRect (state->cgDrawContext, state->clipRect); - CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0); + CGContextSetFillColorWithColor (state->cgDrawContext, transparent); CGContextEOFillPath (state->cgDrawContext); + CGColorRelease (transparent); } @@ -1370,7 +1340,7 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface, unsigned char *imageData; cairo_format_t format; - if (IS_EMPTY (surface)) + if (_cairo_quartz_is_zero_surface (&surface->base)) return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) @@ -1380,8 +1350,9 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface, bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); // let's hope they don't add YUV under us - colorspace = CGBitmapContextGetColorSpace (surface->cgContext); + colorspace = _cairo_quartz_create_color_space (surface->cgContext); color_comps = CGColorSpaceGetNumberOfComponents (colorspace); + CGColorSpaceRelease (colorspace); /* XXX TODO: We can handle many more data formats by * converting to pixman_format_t */ @@ -1442,7 +1413,7 @@ _cairo_quartz_surface_finish (void *abstract_surface) ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); - if (IS_EMPTY (surface)) + if (_cairo_quartz_is_zero_surface (&surface->base)) return CAIRO_STATUS_SUCCESS; /* Restore our saved gstate that we use to reset clipping */ @@ -1904,7 +1875,7 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, cairo_bool_t overlap) { CGAffineTransform textTransform, invTextTransform; - CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGGlyph)]; + CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)]; CGPoint cg_positions_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)]; CGGlyph *cg_glyphs = &glyphs_static[0]; CGPoint *cg_positions = &cg_positions_static[0]; @@ -2089,7 +2060,7 @@ _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path)); - if (IS_EMPTY (surface)) + if (_cairo_quartz_is_zero_surface (&surface->base)) return CAIRO_STATUS_SUCCESS; if (path == NULL) { @@ -2187,7 +2158,7 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, surface->extents.height = height; surface->virtual_extents = surface->extents; - if (IS_EMPTY (surface)) { + if (_cairo_quartz_is_zero_surface (&surface->base)) { surface->cgContext = NULL; surface->cgContextBaseCTM = CGAffineTransformIdentity; surface->base.is_clear = TRUE; @@ -2237,10 +2208,9 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, unsigned int width, unsigned int height) { - cairo_quartz_surface_t *surf; - - surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA, - width, height); + cairo_quartz_surface_t *surf = + _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA, + width, height); if (likely (!surf->base.status)) CGContextRetain (cgContext); @@ -2253,8 +2223,14 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, * @width: width of the surface, in pixels * @height: height of the surface, in pixels * - * Creates a Quartz surface backed by a CGBitmap. The surface is - * created using the Device RGB (or Device Gray, for A8) color space. + * Creates a Quartz surface backed by a CGBitmapContext using the main + * display's colorspace to avoid an expensive colorspace transform + * done serially on the CPU. This may produce slightly different + * colors from what's intended. Programs for which color management is + * important should create their own CGBitmapContext with a + * device-independent color space; most will expect Cairo to draw in + * sRGB and would use CGColorSpaceCreateWithName(kCGColorSpaceSRGB). + * * All Cairo operations, including those that require software * rendering, will succeed on this surface. * @@ -2286,7 +2262,7 @@ cairo_quartz_surface_create (cairo_format_t format, if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) { - cgColorspace = CGColorSpaceCreateDeviceRGB (); + cgColorspace = _cairo_quartz_create_color_space (NULL); bitinfo = kCGBitmapByteOrder32Host; if (format == CAIRO_FORMAT_ARGB32) bitinfo |= kCGImageAlphaPremultipliedFirst; @@ -2375,8 +2351,14 @@ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface) if (surface && _cairo_surface_is_quartz (surface)) { cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface; return quartz->cgContext; - } else - return NULL; + } + + if (surface && _cairo_surface_is_quartz_image (surface)) { + cairo_quartz_image_surface_t *quartz = (cairo_quartz_image_surface_t *) surface; + return quartz->cgContext; + } + + return NULL; } /** @@ -2394,12 +2376,15 @@ _cairo_surface_is_quartz (const cairo_surface_t *surface) } cairo_surface_t* -_cairo_quartz_snapshot_create (cairo_quartz_surface_t *surface) +_cairo_quartz_snapshot_create (cairo_surface_t *surface) { cairo_quartz_snapshot_t *snapshot = NULL; + CGContextRef cgContext; + if (!surface || ! _is_quartz_surface (surface) || _cairo_quartz_is_zero_surface (surface)) + return NULL; - if (!surface || !_cairo_surface_is_quartz (&surface->base) || IS_EMPTY (surface) || - ! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) + if (_cairo_surface_is_quartz (surface) && + ! _cairo_quartz_is_cgcontext_bitmap_context (((cairo_quartz_surface_t*)surface)->cgContext)) return NULL; snapshot = _cairo_malloc (sizeof (cairo_quartz_snapshot_t)); @@ -2408,10 +2393,13 @@ _cairo_quartz_snapshot_create (cairo_quartz_surface_t *surface) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); memset (snapshot, 0, sizeof (cairo_quartz_snapshot_t)); + cgContext = cairo_quartz_surface_get_cg_context (surface); _cairo_surface_init (&snapshot->base, &cairo_quartz_snapshot_backend, NULL, CAIRO_CONTENT_COLOR_ALPHA, FALSE); - snapshot->image = CGBitmapContextCreateImage (surface->cgContext); + snapshot->image = CGBitmapContextCreateImage (cgContext); + _cairo_surface_attach_snapshot (surface, &snapshot->base, NULL); + cairo_surface_destroy (&snapshot->base); // The surface has reffed the snapshot so we must unref it here. return &snapshot->base; } @@ -2426,43 +2414,34 @@ _cairo_quartz_snapshot_finish (void *surface) } CGImageRef -_cairo_quartz_surface_snapshot_get_image (cairo_quartz_surface_t *surface) +_cairo_quartz_surface_snapshot_get_image (cairo_surface_t *surface) { - cairo_surface_t *snapshot = - _cairo_surface_has_snapshot (&surface->base, &cairo_quartz_snapshot_backend); + cairo_surface_t *snapshot; + assert (_is_quartz_surface (surface)); + snapshot = + _cairo_surface_has_snapshot (surface, &cairo_quartz_snapshot_backend); if (unlikely (!snapshot)) { snapshot = _cairo_quartz_snapshot_create (surface); if (unlikely (!snapshot || cairo_surface_status (snapshot))) return NULL; - _cairo_surface_attach_snapshot (&surface->base, snapshot, NULL); - cairo_surface_destroy (snapshot); } return CGImageRetain (((cairo_quartz_snapshot_t*)snapshot)->image); } -/* Debug stuff */ - -#ifdef QUARTZ_DEBUG - void -quartz_image_to_png (CGImageRef image, const char *dest) +_cairo_quartz_image_to_png (CGImageRef image, const char *dest) { - static int sctr = 0; - const char* image_name = "quartz-image"; - char pathbuf[100]; - CFStringRef png_utti = CFSTR("public.png"); CFStringRef path; CFURLRef url; CGImageDestinationRef image_dest; - memset (pathbuf, 0, sizeof (pathbuf)); - dest = dest ? dest : image_name; - snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++); - path = CFStringCreateWithCString (NULL, pathbuf, kCFStringEncodingUTF8); + if (!dest) + return; + path = CFStringCreateWithCString (NULL, dest, kCFStringEncodingUTF8); url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, FALSE); image_dest = CGImageDestinationCreateWithURL (url, png_utti, 1, NULL); @@ -2473,20 +2452,20 @@ quartz_image_to_png (CGImageRef image, const char *dest) CFRelease (path); } -void -quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest) +cairo_status_t +_cairo_quartz_surface_to_png (cairo_surface_t *abstract_surface, const char *dest) { CGImageRef image; - - if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) { - fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq); - return; + cairo_quartz_surface_t *surface; + if (!(_cairo_surface_is_quartz (abstract_surface) && + _cairo_quartz_is_cgcontext_bitmap_context (((cairo_quartz_surface_t*)abstract_surface)->cgContext))) { + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; } - image = CGBitmapContextCreateImage (nq->cgContext); - quartz_image_to_png (image, dest); + surface = (cairo_quartz_surface_t*)abstract_surface; + image = CGBitmapContextCreateImage (surface->cgContext); + _cairo_quartz_image_to_png (image, dest); CGImageRelease (image); + return CAIRO_STATUS_SUCCESS; } - -#endif /* QUARTZ_DEBUG */ diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h index 63b7a1de6..7d4de1ed9 100644 --- a/src/cairo-recording-surface-private.h +++ b/src/cairo-recording-surface-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc @@ -55,14 +56,19 @@ typedef enum { } cairo_command_type_t; typedef enum { - CAIRO_RECORDING_REGION_ALL, + CAIRO_RECORDING_REGION_ALL = 0, CAIRO_RECORDING_REGION_NATIVE, CAIRO_RECORDING_REGION_IMAGE_FALLBACK } cairo_recording_region_type_t; +typedef enum { + CAIRO_RECORDING_REPLAY, + CAIRO_RECORDING_CREATE_REGIONS, + CAIRO_RECORDING_REPLAY_REGION +} cairo_recording_replay_type_t; + typedef struct _cairo_command_header { cairo_command_type_t type; - cairo_recording_region_type_t region; cairo_operator_t op; cairo_rectangle_int_t extents; cairo_clip_t *clip; @@ -155,8 +161,27 @@ typedef struct _cairo_recording_surface { struct bbtree *left, *right; cairo_command_header_t *chain; } bbtree; + + /* The mutex protects modification to all subsequent fields. */ + cairo_mutex_t mutex; + + cairo_list_t region_array_list; + } cairo_recording_surface_t; +typedef struct _cairo_recording_region_element { + cairo_recording_region_type_t region; + unsigned int source_id; + unsigned int mask_id; +} cairo_recording_region_element_t; + +typedef struct _cairo_recording_region_array { + unsigned int id; + cairo_reference_count_t ref_count; + cairo_array_t regions; /* cairo_recording_region_element_t */ + cairo_list_t link; +} cairo_recording_regions_array_t; + slim_hidden_proto (cairo_recording_surface_create); cairo_private cairo_int_status_t @@ -173,26 +198,30 @@ _cairo_recording_surface_replay (cairo_surface_t *surface, cairo_surface_t *target); cairo_private cairo_status_t -_cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surface, - cairo_surface_t *target, - const cairo_color_t *color); +_cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surface, + cairo_surface_t *target, + const cairo_color_t *foreground_color, + cairo_bool_t *foreground_used); cairo_private cairo_status_t _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, const cairo_matrix_t *surface_transform, cairo_surface_t *target, - const cairo_clip_t *target_clip); + const cairo_clip_t *target_clip, + cairo_bool_t surface_is_unbounded); cairo_private cairo_status_t -_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, +_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + unsigned int regions_id, const cairo_matrix_t *surface_transform, - cairo_surface_t *target, - cairo_bool_t surface_is_unbounded); + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded); cairo_private cairo_status_t _cairo_recording_surface_replay_region (cairo_surface_t *surface, - const cairo_rectangle_int_t *surface_extents, + unsigned int regions_id, + const cairo_rectangle_int_t *surface_extents, cairo_surface_t *target, - cairo_recording_region_type_t region); + cairo_recording_region_type_t region); cairo_private cairo_status_t _cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, @@ -210,4 +239,23 @@ _cairo_recording_surface_has_only_bilevel_alpha (cairo_recording_surface_t *surf cairo_private cairo_bool_t _cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface); +cairo_private cairo_status_t +_cairo_recording_surface_region_array_attach (cairo_surface_t *surface, + unsigned int *id); + +cairo_private void +_cairo_recording_surface_region_array_reference (cairo_surface_t *surface, + unsigned int id); + +cairo_private void +_cairo_recording_surface_region_array_remove (cairo_surface_t *surface, + unsigned int id); + +cairo_private void +_cairo_debug_print_recording_surface (FILE *file, + cairo_surface_t *surface, + unsigned int regions_id, + int indent, + cairo_bool_t recurse); + #endif /* CAIRO_RECORDING_SURFACE_H */ diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index 065e62c46..2912f5ede 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -86,16 +86,12 @@ #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-wrapper-private.h" #include "cairo-traps-private.h" -typedef enum { - CAIRO_RECORDING_REPLAY, - CAIRO_RECORDING_CREATE_REGIONS -} cairo_recording_replay_type_t; - typedef struct _cairo_recording_surface_replay_params { const cairo_rectangle_int_t *surface_extents; const cairo_matrix_t *surface_transform; @@ -104,7 +100,9 @@ typedef struct _cairo_recording_surface_replay_params { cairo_bool_t surface_is_unbounded; cairo_recording_replay_type_t type; cairo_recording_region_type_t region; + unsigned int regions_id; const cairo_color_t *foreground_color; + cairo_bool_t foreground_used; } cairo_recording_surface_replay_params_t; static const cairo_surface_backend_t cairo_recording_surface_backend; @@ -366,7 +364,10 @@ _cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface) return CAIRO_STATUS_SUCCESS; cleanup: - bbtree_del (&surface->bbtree); + if (surface->bbtree.left) + bbtree_del (surface->bbtree.left); + if (surface->bbtree.right) + bbtree_del (surface->bbtree.right); return status; } @@ -436,6 +437,10 @@ cairo_recording_surface_create (cairo_content_t content, surface->has_bilevel_alpha = FALSE; surface->has_only_op_over = FALSE; + CAIRO_MUTEX_INIT (surface->mutex); + + cairo_list_init (&surface->region_array_list); + return &surface->base; } slim_hidden_def (cairo_recording_surface_create); @@ -453,12 +458,88 @@ _cairo_recording_surface_create_similar (void *abstract_surface, return cairo_recording_surface_create (content, &extents); } +static void +destroy_pattern_region_array (const cairo_pattern_t *pattern, + unsigned int region_id) +{ + if (region_id != 0) { + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + if (_cairo_surface_is_recording (surface_pattern->surface)) + _cairo_recording_surface_region_array_remove (surface_pattern->surface, region_id); + } + } +} + +static void +_cairo_recording_surface_region_array_destroy (cairo_recording_surface_t *surface, + cairo_recording_regions_array_t *region_array) +{ + cairo_command_t **elements; + cairo_recording_region_element_t *region_elements; + int i, num_elements; + + num_elements = surface->commands.num_elements; + elements = _cairo_array_index (&surface->commands, 0); + region_elements = _cairo_array_index (®ion_array->regions, 0); + for (i = 0; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + cairo_recording_region_element_t *region_element = ®ion_elements[i]; + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + destroy_pattern_region_array (&command->paint.source.base, region_element->source_id); + break; + + case CAIRO_COMMAND_MASK: + destroy_pattern_region_array (&command->mask.source.base, region_element->source_id); + destroy_pattern_region_array (&command->mask.mask.base, region_element->mask_id); + break; + + case CAIRO_COMMAND_STROKE: + destroy_pattern_region_array (&command->stroke.source.base, region_element->source_id); + break; + + case CAIRO_COMMAND_FILL: + destroy_pattern_region_array (&command->fill.source.base, region_element->source_id); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + destroy_pattern_region_array (&command->show_text_glyphs.source.base, region_element->source_id); + break; + + case CAIRO_COMMAND_TAG: + break; + + default: + ASSERT_NOT_REACHED; + } + } + + _cairo_array_fini (®ion_array->regions); + free (region_array); +} + static cairo_status_t _cairo_recording_surface_finish (void *abstract_surface) { cairo_recording_surface_t *surface = abstract_surface; cairo_command_t **elements; int i, num_elements; + cairo_recording_regions_array_t *region_array, *region_next; + + /* Normally backend surfaces hold a reference to the surface as + * well as the region and free the region before the surface. So + * the regions should already be freed at this point but just in + * case we ensure the regions are freed before destroying the + * surface. */ + cairo_list_foreach_entry_safe (region_array, region_next, + cairo_recording_regions_array_t, + &surface->region_array_list, link) + { + cairo_list_del (®ion_array->link); + _cairo_recording_surface_region_array_destroy (surface, region_array); + } num_elements = surface->commands.num_elements; elements = _cairo_array_index (&surface->commands, 0); @@ -614,7 +695,8 @@ _cairo_recording_surface_acquire_source_image (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } - assert (! surface->unbounded); + if (surface->unbounded) + return CAIRO_INT_STATUS_UNSUPPORTED; image = _cairo_image_surface_create_with_content (surface->base.content, surface->extents.width, surface->extents.height); @@ -658,7 +740,6 @@ _command_init (cairo_recording_surface_t *surface, command->type = type; command->op = op; - command->region = CAIRO_RECORDING_REGION_ALL; command->extents = composite ? composite->unbounded : _cairo_empty_rectangle; command->chain = NULL; @@ -1153,6 +1234,14 @@ _cairo_recording_surface_tag (void *abstract_surface, return status; } +static cairo_bool_t +_cairo_recording_surface_supports_color_glyph (void *abstract_surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + return TRUE; +} + static void _command_init_copy (cairo_recording_surface_t *surface, cairo_command_header_t *dst, @@ -1160,7 +1249,6 @@ _command_init_copy (cairo_recording_surface_t *surface, { dst->type = src->type; dst->op = src->op; - dst->region = CAIRO_RECORDING_REGION_ALL; dst->extents = src->extents; dst->chain = NULL; @@ -1559,6 +1647,10 @@ _cairo_recording_surface_snapshot (void *abstract_other) surface->has_bilevel_alpha = other->has_bilevel_alpha; surface->has_only_op_over = other->has_only_op_over; + CAIRO_MUTEX_INIT (surface->mutex); + + cairo_list_init (&surface->region_array_list); + _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); status = _cairo_recording_surface_copy (surface, other); if (unlikely (status)) { @@ -1622,8 +1714,126 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { _cairo_recording_surface_show_text_glyphs, NULL, /* get_supported_mime_types */ _cairo_recording_surface_tag, + _cairo_recording_surface_supports_color_glyph, }; +static unsigned int +_cairo_recording_surface_regions_allocate_unique_id (void) +{ + static cairo_atomic_int_t unique_id; + +#if CAIRO_NO_MUTEX + if (++unique_id == 0) + unique_id = 1; + return unique_id; +#else + cairo_atomic_int_t 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_recording_regions_array_t * +_cairo_recording_surface_region_array_find (cairo_recording_surface_t *surface, + unsigned int id) +{ + cairo_recording_regions_array_t *regions; + + cairo_list_foreach_entry (regions, cairo_recording_regions_array_t, + &surface->region_array_list, link) + { + if (regions->id == id) + return regions; + } + + return NULL; +} + +/* Create and initialize a new #cairo_recording_regions_array_t. Attach + * it to the recording surface and return its id + */ +cairo_status_t +_cairo_recording_surface_region_array_attach (cairo_surface_t *abstract_surface, + unsigned int *id) +{ + cairo_recording_regions_array_t *region_array; + cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface; + + assert (_cairo_surface_is_recording (abstract_surface)); + + region_array = _cairo_malloc (sizeof (cairo_recording_regions_array_t)); + if (region_array == NULL) { + *id = 0; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + region_array->id = _cairo_recording_surface_regions_allocate_unique_id (); + + CAIRO_REFERENCE_COUNT_INIT (®ion_array->ref_count, 1); + + _cairo_array_init (®ion_array->regions, sizeof (cairo_recording_region_element_t)); + + CAIRO_MUTEX_LOCK (surface->mutex); + cairo_list_add (®ion_array->link, &surface->region_array_list); + CAIRO_MUTEX_UNLOCK (surface->mutex); + + *id = region_array->id; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_recording_surface_region_array_remove (cairo_surface_t *abstract_surface, + unsigned int id) +{ + cairo_recording_regions_array_t *region_array; + cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface; + + if (id == 0) + return; + + assert (_cairo_surface_is_recording (abstract_surface)); + + CAIRO_MUTEX_LOCK (surface->mutex); + region_array = _cairo_recording_surface_region_array_find (surface, id); + if (region_array) { + if (_cairo_reference_count_dec_and_test (®ion_array->ref_count)) + cairo_list_del (®ion_array->link); + else + region_array = NULL; + } + + CAIRO_MUTEX_UNLOCK (surface->mutex); + + if (region_array) + _cairo_recording_surface_region_array_destroy (surface, region_array); +} + +void +_cairo_recording_surface_region_array_reference (cairo_surface_t *abstract_surface, + unsigned int id) +{ + cairo_recording_regions_array_t *region_array; + cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface; + + assert (_cairo_surface_is_recording (abstract_surface)); + + CAIRO_MUTEX_LOCK (surface->mutex); + region_array = _cairo_recording_surface_region_array_find (surface, id); + if (region_array) { + _cairo_reference_count_inc (®ion_array->ref_count); + } + + CAIRO_MUTEX_UNLOCK (surface->mutex); +} + cairo_int_status_t _cairo_recording_surface_get_path (cairo_surface_t *abstract_surface, cairo_path_fixed_t *path) @@ -1797,8 +2007,8 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, { cairo_surface_wrapper_t wrapper; cairo_command_t **elements; - cairo_bool_t replay_all = - params->type == CAIRO_RECORDING_CREATE_REGIONS || params->region == CAIRO_RECORDING_REGION_ALL; + cairo_recording_regions_array_t *regions_array = NULL; + cairo_recording_region_element_t *region_elements = NULL; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_rectangle_int_t extents; cairo_bool_t use_indices = FALSE; @@ -1819,6 +2029,11 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, assert (_cairo_surface_is_recording (&surface->base)); + if (params->regions_id != 0) { + regions_array = _cairo_recording_surface_region_array_find (surface, params->regions_id); + assert (regions_array != NULL); + } + _cairo_surface_wrapper_init (&wrapper, params->target); if (params->surface_extents) _cairo_surface_wrapper_intersect_extents (&wrapper, params->surface_extents); @@ -1829,7 +2044,11 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, } _cairo_surface_wrapper_set_inverse_transform (&wrapper, params->surface_transform); _cairo_surface_wrapper_set_clip (&wrapper, params->target_clip); - _cairo_surface_wrapper_set_foreground_color (&wrapper, params->foreground_color); + + if (params->foreground_color) { + params->target->foreground_source = _cairo_pattern_create_solid (params->foreground_color); + params->target->foreground_used = FALSE; + } /* Compute the extents of the target clip in recorded device space */ if (! _cairo_surface_wrapper_get_target_extents (&wrapper, params->surface_is_unbounded, &extents)) @@ -1839,18 +2058,48 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, surface->has_only_op_over = TRUE; num_elements = surface->commands.num_elements; + if (regions_array) { + if (params->type == CAIRO_RECORDING_CREATE_REGIONS) { + /* Re-running create regions with the same region id is not supported. */ + assert (_cairo_array_num_elements (®ions_array->regions) == 0); + void *array_elems; + status = _cairo_array_allocate (®ions_array->regions, num_elements, &array_elems); + if (unlikely (status)) + return status; + + /* Set regions to CAIRO_RECORDING_REGION_ALL and ids to 0 */ + memset (array_elems, 0, num_elements * sizeof (cairo_recording_region_element_t)); + } else { + assert (_cairo_array_num_elements (®ions_array->regions) == num_elements); + } + } + elements = _cairo_array_index (&surface->commands, 0); + if (regions_array) + region_elements = _cairo_array_index (®ions_array->regions, 0); + if (extents.width < r->width || extents.height < r->height) { num_elements = _cairo_recording_surface_get_visible_commands (surface, &extents); use_indices = num_elements != surface->commands.num_elements; } + cairo_bool_t target_is_analysis = _cairo_surface_is_analysis (params->target); + for (i = 0; i < num_elements; i++) { cairo_command_t *command = elements[use_indices ? surface->indices[i] : i]; + cairo_recording_region_element_t *region_element = NULL; + unsigned int source_region_id = 0; + unsigned int mask_region_id = 0; + + if (region_elements) + region_element = ®ion_elements[use_indices ? surface->indices[i] : i]; - if (! replay_all && command->header.region != params->region) + if (region_element && params->type == CAIRO_RECORDING_REPLAY_REGION && + region_element->region != params->region) + { continue; + } if (! _cairo_rectangle_intersects (&extents, &command->header.extents)) { if (command->header.type != CAIRO_COMMAND_TAG) @@ -1859,22 +2108,35 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, switch (command->header.type) { case CAIRO_COMMAND_PAINT: + if (region_element) + source_region_id = region_element->source_id; + status = _cairo_surface_wrapper_paint (&wrapper, command->header.op, &command->paint.source.base, + source_region_id, command->header.clip); if (params->type == CAIRO_RECORDING_CREATE_REGIONS) { _cairo_recording_surface_merge_source_attributes (surface, command->header.op, &command->paint.source.base); + if (region_element && target_is_analysis) + region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target); } break; case CAIRO_COMMAND_MASK: + if (region_element) { + source_region_id = region_element->source_id; + mask_region_id = region_element->mask_id; + } + status = _cairo_surface_wrapper_mask (&wrapper, command->header.op, &command->mask.source.base, + source_region_id, &command->mask.mask.base, + mask_region_id, command->header.clip); if (params->type == CAIRO_RECORDING_CREATE_REGIONS) { _cairo_recording_surface_merge_source_attributes (surface, @@ -1883,13 +2145,21 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, _cairo_recording_surface_merge_source_attributes (surface, command->header.op, &command->mask.mask.base); + if (region_element && target_is_analysis) { + region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target); + region_element->mask_id = _cairo_analysis_surface_get_mask_region_id (params->target); + } } break; case CAIRO_COMMAND_STROKE: + if (region_element) + source_region_id = region_element->source_id; + status = _cairo_surface_wrapper_stroke (&wrapper, command->header.op, &command->stroke.source.base, + source_region_id, &command->stroke.path, &command->stroke.style, &command->stroke.ctm, @@ -1901,23 +2171,39 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, _cairo_recording_surface_merge_source_attributes (surface, command->header.op, &command->stroke.source.base); + if (region_element && target_is_analysis) + region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target); } break; case CAIRO_COMMAND_FILL: status = CAIRO_INT_STATUS_UNSUPPORTED; - if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) { - cairo_command_t *stroke_command; + if (region_element) + source_region_id = region_element->source_id; - stroke_command = NULL; - if (params->type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) + if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) { + cairo_command_t *stroke_command = NULL; + cairo_recording_region_element_t *stroke_region_element = NULL; + unsigned stroke_region_id = 0; + + /* The analysis surface does not implement + * fill_stroke. When creating regions the fill and + * stroke commands are tested separately. + */ + if (params->type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) { stroke_command = elements[i + 1]; + if (region_elements) + stroke_region_element = ®ion_elements[i + 1]; + } - if (stroke_command != NULL && - params->type == CAIRO_RECORDING_REPLAY && + if (stroke_region_element) + stroke_region_id = stroke_region_element->source_id; + + if (stroke_command && stroke_region_element && + params->type == CAIRO_RECORDING_REPLAY_REGION && params->region != CAIRO_RECORDING_REGION_ALL) { - if (stroke_command->header.region != params->region) + if (stroke_region_element->region != params->region) stroke_command = NULL; } @@ -1931,12 +2217,14 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_fill_stroke (&wrapper, command->header.op, &command->fill.source.base, + source_region_id, command->fill.fill_rule, command->fill.tolerance, command->fill.antialias, &command->fill.path, stroke_command->header.op, &stroke_command->stroke.source.base, + stroke_region_id, &stroke_command->stroke.style, &stroke_command->stroke.ctm, &stroke_command->stroke.ctm_inverse, @@ -1958,6 +2246,7 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_fill (&wrapper, command->header.op, &command->fill.source.base, + source_region_id, &command->fill.path, command->fill.fill_rule, command->fill.tolerance, @@ -1967,14 +2256,20 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, _cairo_recording_surface_merge_source_attributes (surface, command->header.op, &command->fill.source.base); + if (region_element && target_is_analysis) + region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target); } } break; case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + if (region_element) + source_region_id = region_element->source_id; + status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, command->header.op, &command->show_text_glyphs.source.base, + source_region_id, command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs, command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, @@ -1985,6 +2280,9 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, _cairo_recording_surface_merge_source_attributes (surface, command->header.op, &command->show_text_glyphs.source.base); + if (region_element && target_is_analysis) + region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target); + } break; @@ -2003,11 +2301,11 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) status = CAIRO_INT_STATUS_SUCCESS; - if (params->type == CAIRO_RECORDING_CREATE_REGIONS && command->header.region != CAIRO_RECORDING_REGION_NATIVE) { + if (params->type == CAIRO_RECORDING_CREATE_REGIONS && region_element) { if (status == CAIRO_INT_STATUS_SUCCESS) { - command->header.region = CAIRO_RECORDING_REGION_NATIVE; + region_element->region = CAIRO_RECORDING_REGION_NATIVE; } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { - command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; + region_element->region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; status = CAIRO_INT_STATUS_SUCCESS; } else { assert (_cairo_int_status_is_error (status)); @@ -2019,6 +2317,12 @@ _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, } done: + if (params->foreground_color) { + cairo_pattern_destroy (params->target->foreground_source); + params->target->foreground_source = NULL; + params->foreground_used = params->target->foreground_used; + } + _cairo_surface_wrapper_fini (&wrapper); return _cairo_surface_set_error (&surface->base, status); } @@ -2030,7 +2334,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, { cairo_surface_wrapper_t wrapper; cairo_command_t **elements, *command; - cairo_int_status_t status; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; if (unlikely (surface->base.status)) return surface->base.status; @@ -2059,6 +2363,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_paint (&wrapper, command->header.op, &command->paint.source.base, + 0, command->header.clip); break; @@ -2066,7 +2371,9 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_mask (&wrapper, command->header.op, &command->mask.source.base, + 0, &command->mask.mask.base, + 0, command->header.clip); break; @@ -2074,6 +2381,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_stroke (&wrapper, command->header.op, &command->stroke.source.base, + 0, &command->stroke.path, &command->stroke.style, &command->stroke.ctm, @@ -2087,6 +2395,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_fill (&wrapper, command->header.op, &command->fill.source.base, + 0, &command->fill.path, command->fill.fill_rule, command->fill.tolerance, @@ -2098,6 +2407,7 @@ _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, command->header.op, &command->show_text_glyphs.source.base, + 0, command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs, command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, @@ -2145,17 +2455,20 @@ _cairo_recording_surface_replay (cairo_surface_t *surface, params.surface_is_unbounded = FALSE; params.type = CAIRO_RECORDING_REPLAY; params.region = CAIRO_RECORDING_REGION_ALL; + params.regions_id = 0; params.foreground_color = NULL; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); } cairo_status_t -_cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surface, - cairo_surface_t *target, - const cairo_color_t *color) +_cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surface, + cairo_surface_t *target, + const cairo_color_t *foreground_color, + cairo_bool_t *foreground_used) { cairo_recording_surface_replay_params_t params; + cairo_status_t status; params.surface_extents = NULL; params.surface_transform = NULL; @@ -2164,16 +2477,22 @@ _cairo_recording_surface_replay_with_foreground_color (cairo_surface_t *surface, params.surface_is_unbounded = FALSE; params.type = CAIRO_RECORDING_REPLAY; params.region = CAIRO_RECORDING_REGION_ALL; - params.foreground_color = color; + params.regions_id = 0; + params.foreground_color = foreground_color; + params.foreground_used = FALSE; - return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); + status = _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); + *foreground_used = params.foreground_used; + + return status; } cairo_status_t _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, const cairo_matrix_t *surface_transform, cairo_surface_t *target, - const cairo_clip_t *target_clip) + const cairo_clip_t *target_clip, + cairo_bool_t surface_is_unbounded) { cairo_recording_surface_replay_params_t params; @@ -2181,9 +2500,10 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, params.surface_transform = surface_transform; params.target = target; params.target_clip = target_clip; - params.surface_is_unbounded = FALSE; + params.surface_is_unbounded = surface_is_unbounded; params.type = CAIRO_RECORDING_REPLAY; params.region = CAIRO_RECORDING_REGION_ALL; + params.regions_id = 0; params.foreground_color = NULL; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); @@ -2197,6 +2517,7 @@ _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, */ cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + unsigned int regions_id, const cairo_matrix_t *surface_transform, cairo_surface_t *target, cairo_bool_t surface_is_unbounded) @@ -2210,6 +2531,7 @@ _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, params.surface_is_unbounded = surface_is_unbounded; params.type = CAIRO_RECORDING_CREATE_REGIONS; params.region = CAIRO_RECORDING_REGION_ALL; + params.regions_id = regions_id; params.foreground_color = NULL; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); @@ -2217,6 +2539,7 @@ _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, cairo_status_t _cairo_recording_surface_replay_region (cairo_surface_t *surface, + unsigned int regions_id, const cairo_rectangle_int_t *surface_extents, cairo_surface_t *target, cairo_recording_region_type_t region) @@ -2228,8 +2551,9 @@ _cairo_recording_surface_replay_region (cairo_surface_t *surface, params.target = target; params.target_clip = NULL; params.surface_is_unbounded = FALSE; - params.type = CAIRO_RECORDING_REPLAY; + params.type = CAIRO_RECORDING_REPLAY_REGION; params.region = region; + params.regions_id = regions_id; params.foreground_color = NULL; return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, ¶ms); @@ -2245,7 +2569,7 @@ _recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, cairo_status_t status; null_surface = _cairo_null_surface_create (surface->base.content); - analysis_surface = _cairo_analysis_surface_create (null_surface); + analysis_surface = _cairo_analysis_surface_create (null_surface, FALSE); cairo_surface_destroy (null_surface); status = analysis_surface->status; @@ -2309,6 +2633,7 @@ DONE: if (height) *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); } +slim_hidden_def (cairo_recording_surface_ink_extents); cairo_status_t _cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, @@ -2376,3 +2701,186 @@ _cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface) { return surface->has_only_op_over; } + +static void +print_indent (FILE *file, int indent) +{ + fprintf (file, "%*s", indent * 2, ""); +} + +static void +print_pattern (FILE *file, + const cairo_pattern_t *pattern, + unsigned int region_id, + int indent, + cairo_bool_t recurse) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: { + cairo_solid_pattern_t *p = (cairo_solid_pattern_t *) pattern; + if (pattern->is_foreground_marker) { + fprintf (file, "solid foreground\n"); + } else { + fprintf (file, "solid rgba: %f %f %f %f\n", + p->color.red, + p->color.green, + p->color.blue, + p->color.alpha); + } + } break; + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *p = (cairo_surface_pattern_t *) pattern; + fprintf (file, "surface "); + if (p->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + fprintf (file, "recording id: %d\n", p->surface->unique_id); + if (recurse) { + _cairo_debug_print_recording_surface (file, p->surface, + region_id, + indent + 1, recurse); + } + } else if (p->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *image = (cairo_image_surface_t *)p->surface; + fprintf (file, "image format: "); + switch (image->format) { + case CAIRO_FORMAT_INVALID: fputs ("INVALID", file); break; + case CAIRO_FORMAT_ARGB32: fputs ("ARGB32", file); break; + case CAIRO_FORMAT_RGB24: fputs ("RGB24", file); break; + case CAIRO_FORMAT_A8: fputs ("A8", file); break; + case CAIRO_FORMAT_A1: fputs ("A1", file); break; + case CAIRO_FORMAT_RGB16_565: fputs ("RGB16_565", file); break; + case CAIRO_FORMAT_RGB30: fputs ("RGB30", file); break; + case CAIRO_FORMAT_RGB96F: fputs ("RGB96F", file); break; + case CAIRO_FORMAT_RGBA128F: fputs ("RGBA128F", file); break; + } + fprintf (file, " width: %d height: %d\n", image->width, image->height); + } else { + fprintf (file, "type %d\n", p->surface->type); + } + } break; + case CAIRO_PATTERN_TYPE_LINEAR: + fprintf (file, "linear\n"); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + fprintf (file, "radial\n"); + break; + case CAIRO_PATTERN_TYPE_MESH: + fprintf (file, "mesh\n"); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + fprintf (file, "raster\n"); + break; + } +} + +void +_cairo_debug_print_recording_surface (FILE *file, + cairo_surface_t *surface, + unsigned int regions_id, + int indent, + cairo_bool_t recurse) +{ + cairo_command_t **elements; + cairo_recording_region_element_t *region_elements = NULL; + unsigned int i, num_elements; + cairo_recording_surface_t *recording_surface; + cairo_surface_t *free_me = NULL; + char common[100]; + + if (_cairo_surface_is_snapshot (surface)) + free_me = surface = _cairo_surface_snapshot_get_target (surface); + + assert (_cairo_surface_is_recording (surface)); + recording_surface = (cairo_recording_surface_t *)surface; + + print_indent (file, indent); + indent++; + fprintf(file, "recording surface id: %d regions id: %d\n", recording_surface->base.unique_id, regions_id); + num_elements = recording_surface->commands.num_elements; + elements = _cairo_array_index (&recording_surface->commands, 0); + + if (regions_id != 0) { + cairo_recording_regions_array_t *regions_array; + regions_array = _cairo_recording_surface_region_array_find (recording_surface, regions_id); + assert (regions_array != NULL); + assert (_cairo_array_num_elements (®ions_array->regions) == num_elements); + region_elements = _cairo_array_index (®ions_array->regions, 0); + } + + for (i = 0; i < num_elements; i++) { + cairo_command_t *command = elements[i]; + unsigned int source_region_id = 0; + unsigned int mask_region_id = 0; + + common[0] = 0; + if (region_elements) { + cairo_recording_region_element_t *region_element = ®ion_elements[i]; + strcpy (common, "region: "); + switch (region_element->region) { + case CAIRO_RECORDING_REGION_ALL: strcat (common, "all"); break; + case CAIRO_RECORDING_REGION_NATIVE: strcat (common, "native"); break; + case CAIRO_RECORDING_REGION_IMAGE_FALLBACK: strcat (common, "fallback"); break; + } + source_region_id = region_element->source_id; + mask_region_id = region_element->mask_id; + } + sprintf (common + strlen(common), " op: %s", _cairo_debug_operator_to_string (command->header.op)); + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + print_indent (file, indent); + fprintf(file, "%d PAINT %s source: ", i, common); + print_pattern (file, &command->paint.source.base, source_region_id, indent + 1, recurse); + break; + + case CAIRO_COMMAND_MASK: + print_indent (file, indent); + fprintf(file, "%d MASK %s\n", i, common); + print_indent (file, indent + 1); + fprintf(file, "source: "); + print_pattern (file, &command->mask.source.base, source_region_id, indent + 1, recurse); + print_indent (file, indent + 1); + fprintf(file, "mask: "); + print_pattern (file, &command->mask.mask.base, mask_region_id, indent + 1, recurse); + break; + + case CAIRO_COMMAND_STROKE: + print_indent (file, indent); + fprintf(file, "%d STROKE %s source:", i, common); + print_pattern (file, &command->stroke.source.base, source_region_id, indent + 1, recurse); + break; + + case CAIRO_COMMAND_FILL: + print_indent (file, indent); + fprintf(file, "%d FILL %s source: ", i, common); + print_pattern (file, &command->fill.source.base, source_region_id, indent + 1, recurse); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + print_indent (file, indent); + fprintf(file, "%d SHOW_TEXT_GLYPHS %s font_type: ", i, common); + switch (command->show_text_glyphs.scaled_font->backend->type) { + case CAIRO_FONT_TYPE_TOY: fputs ("toy", file); break; + case CAIRO_FONT_TYPE_FT: fputs ("ft", file); break; + case CAIRO_FONT_TYPE_WIN32: fputs ("win32", file); break; + case CAIRO_FONT_TYPE_QUARTZ: fputs ("quartz", file); break; + case CAIRO_FONT_TYPE_USER: fputs ("user", file); break; + case CAIRO_FONT_TYPE_DWRITE: fputs ("dwrite", file); break; + } + fprintf (file, " glyphs:"); + for (unsigned j = 0; j < command->show_text_glyphs.num_glyphs; j++) + fprintf (file, " %ld", command->show_text_glyphs.glyphs[j].index); + fprintf (file, " source:"); + print_pattern (file, &command->show_text_glyphs.source.base, source_region_id, indent + 1, recurse); + break; + + case CAIRO_COMMAND_TAG: + print_indent (file, indent); + fprintf(file, "%d TAG\n", i); + break; + + default: + ASSERT_NOT_REACHED; + } + } + cairo_surface_destroy (free_me); +} diff --git a/src/cairo-region.c b/src/cairo-region.c index eb78cf4a8..d38f50d92 100644 --- a/src/cairo-region.c +++ b/src/cairo-region.c @@ -112,6 +112,7 @@ _cairo_region_create_in_error (cairo_status_t status) case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_STATUS_TAG_ERROR: case CAIRO_STATUS_DWRITE_ERROR: + case CAIRO_STATUS_SVG_FONT_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_region_t *) &_cairo_region_nil; diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h index 6fd772bdb..64abbe0f5 100644 --- a/src/cairo-scaled-font-private.h +++ b/src/cairo-scaled-font-private.h @@ -107,7 +107,7 @@ struct _cairo_scaled_font { cairo_font_extents_t fs_extents; /* font space */ /* The mutex protects modification to all subsequent fields. */ - cairo_mutex_t mutex; + cairo_recursive_mutex_t mutex; cairo_hash_table_t *glyphs; cairo_list_t glyph_pages; @@ -149,8 +149,12 @@ struct _cairo_scaled_glyph { cairo_list_t dev_privates; cairo_color_t foreground_color; /* only used for color glyphs */ - /* TRUE if the color_surface required the foreground_color to render. */ - unsigned uses_foreground_color : 1; + + /* TRUE if the recording_surface used the foreground_source to render. */ + unsigned recording_uses_foreground_color : 1; + + /* TRUE if the recording surface uses the foreground marker. */ + unsigned recording_uses_foreground_marker : 1; /* TRUE if color_glyph specifies if glyph is color or non color, FALSE if glyph color type unknown. */ unsigned color_glyph_set : 1; diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h index 5b531d298..2599bb6c3 100644 --- a/src/cairo-scaled-font-subsets-private.h +++ b/src/cairo-scaled-font-subsets-private.h @@ -204,7 +204,7 @@ _cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *fon * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain * the x and y advance for the mapped glyph in device space. * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for - * the the mapped glyph from an unhinted 1 point font. + * the mapped glyph from an unhinted 1 point font. * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph() * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already * mapped to a different utf8 string. @@ -297,41 +297,6 @@ _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t void *closure); /** - * _cairo_scaled_font_subsets_foreach_user: - * @font_subsets: a #cairo_scaled_font_subsets_t - * @font_subset_callback: a function to be called for each font subset - * @closure: closure data for the callback function - * - * Iterate over each unique scaled font subset as created by calls to - * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by - * unique pairs of (font_id, subset_id) as returned by - * _cairo_scaled_font_subsets_map_glyph(). - * - * For each subset, @font_subset_callback will be called and will be - * provided with both a #cairo_scaled_font_subset_t object containing - * all the glyphs in the subset as well as the value of @closure. - * - * The #cairo_scaled_font_subset_t object contains the scaled_font, - * the font_id, and the subset_id corresponding to all glyphs - * belonging to the subset. In addition, it contains an array providing - * a mapping between subset glyph indices and the original scaled font - * glyph indices. - * - * The index of the array corresponds to subset_glyph_index values - * returned by _cairo_scaled_font_subsets_map_glyph() while the - * values of the array correspond to the scaled_font_glyph_index - * values passed as input to the same function. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero - * value indicating an error. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_status_t -_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure); - -/** * _cairo_scaled_font_subset_create_glyph_names: * @font_subsets: a #cairo_scaled_font_subsets_t * diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c index 94a7aae26..2a9e8144c 100644 --- a/src/cairo-scaled-font-subsets.c +++ b/src/cairo-scaled-font-subsets.c @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California @@ -61,7 +62,6 @@ typedef enum { typedef enum { CAIRO_SUBSETS_FOREACH_UNSCALED, CAIRO_SUBSETS_FOREACH_SCALED, - CAIRO_SUBSETS_FOREACH_USER } cairo_subsets_foreach_type_t; typedef struct _cairo_sub_font { @@ -69,7 +69,6 @@ typedef struct _cairo_sub_font { cairo_bool_t is_scaled; cairo_bool_t is_composite; - cairo_bool_t is_user; cairo_bool_t use_latin_subset; cairo_bool_t reserve_notdef; cairo_scaled_font_subsets_t *parent; @@ -283,8 +282,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, sub_font->is_scaled = is_scaled; sub_font->is_composite = is_composite; - sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face); - sub_font->reserve_notdef = !sub_font->is_user; + sub_font->reserve_notdef = !sub_font->is_scaled; _cairo_sub_font_init_key (sub_font, scaled_font); sub_font->parent = parent; @@ -294,7 +292,7 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, sub_font->use_latin_subset = parent->use_latin_subset; /* latin subsets of Type 3 and CID CFF fonts are not supported */ - if (sub_font->is_user || sub_font->is_scaled || + if (sub_font->is_scaled || _cairo_cff_scaled_font_is_cid_cff (scaled_font) ) { sub_font->use_latin_subset = FALSE; @@ -404,12 +402,10 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_scaled_font_t *scaled_font, if (unicode != (uint32_t) -1) { len = _cairo_ucs4_to_utf8 (unicode, buf); if (len > 0) { - *utf8_out = _cairo_malloc (len + 1); - if (unlikely (*utf8_out == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + *utf8_out = _cairo_strndup (buf, len); + if (unlikely (*utf8_out == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - memcpy (*utf8_out, buf, len); - (*utf8_out)[len] = 0; *utf8_len_out = len; } } @@ -441,12 +437,10 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, } } else { /* No existing mapping. Use the requested mapping */ - sub_font_glyph->utf8 = _cairo_malloc (utf8_len + 1); - if (unlikely (sub_font_glyph->utf8 == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + sub_font_glyph->utf8 = _cairo_strndup (utf8, utf8_len); + if (unlikely (sub_font_glyph->utf8 == NULL)) + return _cairo_error (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; *is_mapped = TRUE; } @@ -612,25 +606,22 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, if (ucs4_len == 1) { font_unicode = ucs4[0]; free (font_utf8); - font_utf8 = _cairo_malloc (text_utf8_len + 1); - if (font_utf8 == NULL) { - free (ucs4); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + font_utf8 = _cairo_strndup (text_utf8, text_utf8_len); + if (font_utf8 == NULL) { + free (ucs4); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - memcpy (font_utf8, text_utf8, text_utf8_len); - font_utf8[text_utf8_len] = 0; font_utf8_len = text_utf8_len; } free (ucs4); } } - /* If glyph is in the winansi encoding and font is not a user + /* If glyph is in the winansi encoding and font is not a scaled * font, put glyph in the latin subset. */ is_latin = FALSE; latin_character = -1; - if (sub_font->use_latin_subset && - (! _cairo_font_face_is_user (sub_font->scaled_font->font_face))) + if (sub_font->use_latin_subset && !sub_font->is_scaled) { latin_character = _cairo_unicode_to_winansi (font_unicode); if (latin_character > 0) @@ -844,6 +835,9 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, cairo_int_status_t status; int max_glyphs; cairo_bool_t type1_font; + cairo_bool_t has_path; + cairo_bool_t has_color; + cairo_bool_t is_user; /* Lookup glyph in unscaled subsets */ if (subsets->type != CAIRO_SUBSETS_SCALED) { @@ -877,30 +871,47 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, /* Glyph not found. Determine whether the glyph is outline or * bitmap and add to the appropriate subset. - * - * glyph_index 0 (the .notdef glyph) is a special case. Some fonts + */ + is_user = _cairo_font_face_is_user (scaled_font->font_face); + _cairo_scaled_font_freeze_cache (scaled_font); + /* Check if glyph is color */ + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); + has_color = (status == CAIRO_INT_STATUS_SUCCESS); + + /* Check if glyph has a path */ + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_PATH, + NULL, /* foreground color */ + &scaled_glyph); + has_path = (status == CAIRO_INT_STATUS_SUCCESS); + + /* glyph_index 0 (the .notdef glyph) is a special case. Some fonts * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates * empty glyphs in this case so we can put the glyph in a unscaled - * subset. */ - if (scaled_font_glyph_index == 0 || - _cairo_font_face_is_user (scaled_font->font_face)) { - status = CAIRO_STATUS_SUCCESS; - } else { - _cairo_scaled_font_freeze_cache (scaled_font); - status = _cairo_scaled_glyph_lookup (scaled_font, - scaled_font_glyph_index, - CAIRO_SCALED_GLYPH_INFO_PATH, + * subset. + */ + if (scaled_font_glyph_index == 0 && !is_user) + has_path = TRUE; + + /* If this fails there is nothing we can do with this glyph. */ + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, NULL, /* foreground color */ - &scaled_glyph); - _cairo_scaled_font_thaw_cache (scaled_font); - } + &scaled_glyph); + _cairo_scaled_font_thaw_cache (scaled_font); if (_cairo_int_status_is_error (status)) return status; - if (status == CAIRO_INT_STATUS_SUCCESS && - subsets->type != CAIRO_SUBSETS_SCALED && - ! _cairo_font_face_is_user (scaled_font->font_face)) + /* Type 3 glyphs (is_user and has_color) must be added to scaled subset */ + if (subsets->type != CAIRO_SUBSETS_SCALED && + has_path && !has_color && !is_user) { /* Path available. Add to unscaled subset. */ key.is_scaled = FALSE; @@ -1012,19 +1023,12 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t { cairo_sub_font_collection_t collection; cairo_sub_font_t *sub_font; - cairo_bool_t is_scaled, is_user; + cairo_bool_t is_scaled; is_scaled = FALSE; - is_user = FALSE; - if (type == CAIRO_SUBSETS_FOREACH_USER) - is_user = TRUE; - - if (type == CAIRO_SUBSETS_FOREACH_SCALED || - type == CAIRO_SUBSETS_FOREACH_USER) - { + if (type == CAIRO_SUBSETS_FOREACH_SCALED) is_scaled = TRUE; - } if (is_scaled) collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used; @@ -1060,9 +1064,7 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t sub_font = font_subsets->unscaled_sub_fonts_list; while (sub_font) { - if (sub_font->is_user == is_user) - _cairo_sub_font_collect (sub_font, &collection); - + _cairo_sub_font_collect (sub_font, &collection); sub_font = sub_font->next; } free (collection.utf8); @@ -1095,17 +1097,6 @@ _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *fo CAIRO_SUBSETS_FOREACH_UNSCALED); } -cairo_status_t -_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, - cairo_scaled_font_subset_callback_func_t font_subset_callback, - void *closure) -{ - return _cairo_scaled_font_subsets_foreach_internal (font_subsets, - font_subset_callback, - closure, - CAIRO_SUBSETS_FOREACH_USER); -} - static cairo_bool_t _cairo_string_equal (const void *key_a, const void *key_b) { diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index 5fe81110f..75640f723 100755 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -788,7 +788,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_font_face_reference (font_face); scaled_font->original_font_face = NULL; - CAIRO_MUTEX_INIT (scaled_font->mutex); + CAIRO_RECURSIVE_MUTEX_INIT (scaled_font->mutex); cairo_list_init (&scaled_font->dev_privates); @@ -1176,9 +1176,7 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, status = font_face->backend->scaled_font_create (font_face, font_matrix, ctm, options, &scaled_font); - /* Did we leave the backend in an error state? */ if (unlikely (status)) { - status = _cairo_font_face_set_error (font_face, status); _cairo_scaled_font_map_unlock (); if (font_face != original_font_face) cairo_font_face_destroy (font_face); @@ -2655,10 +2653,21 @@ _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH; } +/** + * _cairo_scaled_glyph_set_recording_surface: + * @scaled_glyph: a #cairo_scaled_glyph_t + * @scaled_font: a #cairo_scaled_font_t + * @recording_surface: The recording surface + * @foreground_color: The foreground color that was used to record the + * glyph, or NULL if foreground color not required. + * + * Sets the surface that was used to record the glyph. + */ void _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, - cairo_surface_t *recording_surface) + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface, + const cairo_color_t * foreground_color) { if (scaled_glyph->recording_surface != NULL) { cairo_surface_finish (scaled_glyph->recording_surface); @@ -2666,6 +2675,9 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, } scaled_glyph->recording_surface = recording_surface; + scaled_glyph->recording_uses_foreground_color = foreground_color != NULL; + if (foreground_color) + scaled_glyph->foreground_color = *foreground_color; if (recording_surface != NULL) scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; @@ -2673,11 +2685,22 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; } +/** + * _cairo_scaled_glyph_set_color_surface: + * @scaled_glyph: a #cairo_scaled_glyph_t + * @scaled_font: a #cairo_scaled_font_t + * @surface: The image surface + * @foreground_marker_color: The foreground color that was used to + * substitute the foreground_marker, or NULL if foreground_marker not + * used when rendering the surface color. + * + * Sets the color surface of the glyph. + */ void -_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font, +_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, cairo_image_surface_t *surface, - cairo_bool_t uses_foreground_color) + const cairo_color_t *foreground_marker_color) { if (scaled_glyph->color_surface != NULL) cairo_surface_destroy (&scaled_glyph->color_surface->base); @@ -2685,7 +2708,9 @@ _cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph, /* sanity check the backend glyph contents */ _cairo_debug_check_image_surface_is_defined (&surface->base); scaled_glyph->color_surface = surface; - scaled_glyph->uses_foreground_color = uses_foreground_color; + scaled_glyph->recording_uses_foreground_marker = foreground_marker_color != NULL; + if (foreground_marker_color) + scaled_glyph->foreground_color = *foreground_marker_color; if (surface != NULL) scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; @@ -2703,7 +2728,16 @@ _cairo_scaled_glyph_page_can_remove (const void *closure) cairo_scaled_font_t *scaled_font; scaled_font = page->scaled_font; - return CAIRO_MUTEX_TRY_LOCK (scaled_font->mutex); + + if (!CAIRO_MUTEX_TRY_LOCK (scaled_font->mutex)) + return FALSE; + + if (scaled_font->cache_frozen != 0) { + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + return FALSE; + } + + return TRUE; } static cairo_status_t @@ -2807,8 +2841,11 @@ _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, * @index: the glyph to create * @info: a #cairo_scaled_glyph_info_t marking which portions of * the glyph should be filled in. - * @foreground_color - foreground color to use when rendering color fonts. Use NULL - * if not requesting CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE or foreground color is unknown. + * @foreground_color - foreground color to use when rendering color + * fonts. Use NULL if not requesting + * CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE or + * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, or foreground color is + * unknown. * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph * is returned. * @@ -2902,14 +2939,24 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, scaled_glyph->color_glyph_set && !scaled_glyph->color_glyph) return CAIRO_INT_STATUS_UNSUPPORTED; - /* If requesting a color surface for a glyph that has used the - * foreground color to render the color_surface, and the + /* If requesting a color surface or recording for a glyph that has + * used the foreground color to render the recording, and the + * foreground color has changed, request a new recording. */ + if ((info & (CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) && + scaled_glyph->recording_uses_foreground_color && + !_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color)) + { + need_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; + } + + /* If requesting a color surface for a glyph that has + * used the foreground color to render the color_surface, and the * foreground color has changed, request a new image. */ - if ((info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) && - scaled_glyph->uses_foreground_color && + if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE && + (scaled_glyph->recording_uses_foreground_marker || scaled_glyph->recording_uses_foreground_color) && !_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color)) { - need_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; + need_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; } if (need_info) { @@ -3066,6 +3113,7 @@ cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, return; } + _cairo_font_options_fini (options); _cairo_font_options_init_copy (options, &scaled_font->options); } slim_hidden_def (cairo_scaled_font_get_font_options); diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index b96d7d182..058626321 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -1882,7 +1882,7 @@ _emit_path_boxes (cairo_script_surface_t *surface, if (! _cairo_path_fixed_iter_at_end (&iter)) { _cairo_boxes_fini (&boxes); - return CAIRO_STATUS_INVALID_PATH_DATA; + return CAIRO_INT_STATUS_UNSUPPORTED; } for (chunk = &boxes.chunks; chunk; chunk = chunk->next) { @@ -2113,6 +2113,16 @@ _device_flush (void *abstract_device) } static void +_device_finish (void *abstract_device) +{ + cairo_script_context_t *ctx = abstract_device; + + cairo_status_t status = _cairo_output_stream_close (ctx->stream); + status = _cairo_device_set_error (&ctx->base, status); + (void) status; +} + +static void _device_destroy (void *abstract_device) { cairo_script_context_t *ctx = abstract_device; @@ -2499,7 +2509,7 @@ _cairo_script_surface_paint (void *abstract_surface, if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_paint (&surface->wrapper, - op, source, clip); + op, source, 0, clip); } return CAIRO_STATUS_SUCCESS; @@ -2556,7 +2566,7 @@ _cairo_script_surface_mask (void *abstract_surface, if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_mask (&surface->wrapper, - op, source, mask, clip); + op, source, 0, mask, 0, clip); } return CAIRO_STATUS_SUCCESS; @@ -2643,7 +2653,7 @@ _cairo_script_surface_stroke (void *abstract_surface, if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_stroke (&surface->wrapper, - op, source, path, + op, source, 0, path, style, ctm, ctm_inverse, tolerance, antialias, @@ -2724,7 +2734,7 @@ _cairo_script_surface_fill (void *abstract_surface, if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_fill (&surface->wrapper, - op, source, path, + op, source, 0, path, fill_rule, tolerance, antialias, @@ -3034,6 +3044,7 @@ _emit_scaled_font (cairo_script_surface_t *surface, if (unlikely (status)) return status; + _cairo_font_options_init_default (&options); cairo_scaled_font_get_font_options (scaled_font, &options); status = _emit_font_options (surface, &options); if (unlikely (status)) @@ -3575,7 +3586,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, - op, source, + op, source, 0, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, @@ -3731,7 +3742,7 @@ static const cairo_device_backend_t _cairo_script_device_backend = { NULL, NULL, /* lock, unlock */ _device_flush, /* flush */ - NULL, /* finish */ + _device_finish, /* finish */ _device_destroy }; diff --git a/src/cairo-shape-mask-compositor.c b/src/cairo-shape-mask-compositor.c index 3117267cc..0f4918603 100644 --- a/src/cairo-shape-mask-compositor.c +++ b/src/cairo-shape-mask-compositor.c @@ -116,7 +116,7 @@ _cairo_shape_mask_compositor_stroke (const cairo_compositor_t *_compositor, &_cairo_pattern_white.base, &pattern.base, clip); - if ((status == CAIRO_INT_STATUS_SUCCESS)) { + if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_ADD, &extents->source_pattern.base, @@ -210,7 +210,7 @@ _cairo_shape_mask_compositor_fill (const cairo_compositor_t *_compositor, &_cairo_pattern_white.base, &pattern.base, clip); - if ((status == CAIRO_INT_STATUS_SUCCESS)) { + if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_ADD, &extents->source_pattern.base, @@ -303,7 +303,7 @@ _cairo_shape_mask_compositor_glyphs (const cairo_compositor_t *_compositor, &_cairo_pattern_white.base, &pattern.base, clip); - if ((status == CAIRO_INT_STATUS_SUCCESS)) { + if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_ADD, &extents->source_pattern.base, diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c index 50c92b25c..49c5999d2 100644 --- a/src/cairo-spans-compositor.c +++ b/src/cairo-spans-compositor.c @@ -612,7 +612,7 @@ composite_aligned_boxes (const cairo_spans_compositor_t *compositor, recording_clip = _cairo_clip_from_boxes (boxes); status = _cairo_recording_surface_replay_with_clip (unwrap_source (source), - m, dst, recording_clip); + m, dst, recording_clip, FALSE); _cairo_clip_destroy (recording_clip); return status; diff --git a/src/cairo-spans.c b/src/cairo-spans.c index 1b46adf4d..711c0c106 100644 --- a/src/cairo-spans.c +++ b/src/cairo-spans.c @@ -133,6 +133,7 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_STATUS_TAG_ERROR: case CAIRO_STATUS_DWRITE_ERROR: + case CAIRO_STATUS_SVG_FONT_ERROR: default: break; } @@ -251,6 +252,7 @@ _cairo_span_renderer_create_in_error (cairo_status_t status) case CAIRO_STATUS_WIN32_GDI_ERROR: RETURN_NIL; case CAIRO_STATUS_TAG_ERROR: RETURN_NIL; case CAIRO_STATUS_DWRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SVG_FONT_ERROR: RETURN_NIL; default: break; } diff --git a/src/cairo-spline.c b/src/cairo-spline.c index 44634faec..6f50a637a 100644 --- a/src/cairo-spline.c +++ b/src/cairo-spline.c @@ -177,7 +177,7 @@ _cairo_spline_error_squared (const cairo_spline_knots_t *knots) double bdx, bdy, berr; double cdx, cdy, cerr; - /* We are going to compute the distance (squared) between each of the the b + /* We are going to compute the distance (squared) between each of the b * and c control points and the segment a-b. The maximum of these two * distances will be our approximation error. */ diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h index d31655be8..15032de20 100644 --- a/src/cairo-surface-backend-private.h +++ b/src/cairo-surface-backend-private.h @@ -124,14 +124,14 @@ struct _cairo_surface_backend { (*paint) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*mask) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*stroke) (void *surface, @@ -143,7 +143,7 @@ struct _cairo_surface_backend { const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill) (void *surface, @@ -153,7 +153,7 @@ struct _cairo_surface_backend { cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - const cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill_stroke) (void *surface, @@ -196,7 +196,7 @@ struct _cairo_surface_backend { int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip); + const cairo_clip_t *clip); const char ** (*get_supported_mime_types) (void *surface); @@ -207,6 +207,10 @@ struct _cairo_surface_backend { const char *tag_name, const char *attributes); + cairo_bool_t + (*supports_color_glyph) (void *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index); }; cairo_private cairo_status_t diff --git a/src/cairo-surface-observer.c b/src/cairo-surface-observer.c index 9c4432e24..bf29d4219 100644 --- a/src/cairo-surface-observer.c +++ b/src/cairo-surface-observer.c @@ -54,6 +54,15 @@ #include "cairo-script-private.h" #endif +/** + * SECTION:cairo-surface-observer + * @Title: Surface Observer + * @Short_Description: Observing other surfaces + * @See_Also: #cairo_surface_t + * + * A surface that exists solely to watch another is doing. + */ + static const cairo_surface_backend_t _cairo_surface_observer_backend; /* observation/stats */ diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h index e4ad5f3b1..35b559f9c 100644 --- a/src/cairo-surface-private.h +++ b/src/cairo-surface-private.h @@ -104,6 +104,9 @@ struct _cairo_surface { * cairo_surface_create_similar(). */ cairo_font_options_t font_options; + + cairo_pattern_t *foreground_source; + cairo_bool_t foreground_used; }; cairo_private cairo_surface_t * diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h index 7c3bc56ba..016402d7e 100644 --- a/src/cairo-surface-wrapper-private.h +++ b/src/cairo-surface-wrapper-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California @@ -41,6 +42,7 @@ #include "cairoint.h" #include "cairo-types-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-surface-backend-private.h" CAIRO_BEGIN_DECLS @@ -53,7 +55,9 @@ struct _cairo_surface_wrapper { cairo_bool_t has_extents; cairo_rectangle_int_t extents; const cairo_clip_t *clip; - cairo_pattern_t *foreground_source; + + unsigned int source_region_id; + unsigned int mask_region_id; cairo_bool_t needs_transform; }; @@ -75,10 +79,6 @@ _cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper, const cairo_clip_t *clip); cairo_private void -_cairo_surface_wrapper_set_foreground_color (cairo_surface_wrapper_t *wrapper, - const cairo_color_t *color); - -cairo_private void _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper); static inline cairo_bool_t @@ -100,60 +100,68 @@ _cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, cairo_private cairo_status_t _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip); + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip); + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_pattern_t *mask, + unsigned int mask_region_id, + const cairo_clip_t *clip); cairo_private cairo_status_t -_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip); +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip); cairo_private cairo_status_t -_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - const cairo_path_fixed_t*path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - const cairo_clip_t *clip); +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + unsigned int fill_region_id, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + const cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + unsigned int stroke_region_id, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + const cairo_clip_t *clip); cairo_private cairo_status_t -_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip); +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip); cairo_private cairo_status_t -_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, + unsigned int source_region_id, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c index 8ba82bd40..29c19c5a5 100644 --- a/src/cairo-surface-wrapper.c +++ b/src/cairo-surface-wrapper.c @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc @@ -47,12 +48,18 @@ static void _copy_transformed_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original, - const cairo_matrix_t *ctm_inverse) + const cairo_matrix_t *ctm_inverse, + unsigned int region_id) { _cairo_pattern_init_static_copy (pattern, original); if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + surface_pattern->region_array_id = region_id; + } } cairo_status_t @@ -129,9 +136,10 @@ _cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper, cairo_status_t _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; @@ -144,10 +152,7 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (source->is_userfont_foreground && wrapper->foreground_source) - source = wrapper->foreground_source; - - if (wrapper->needs_transform) { + if (wrapper->needs_transform || source_region_id != 0) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); @@ -155,7 +160,7 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); - _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id); source = &source_copy.base; } @@ -165,13 +170,14 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, return status; } - cairo_status_t _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip) + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_pattern_t *mask, + unsigned int mask_region_id, + const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; @@ -185,10 +191,7 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (source->is_userfont_foreground && wrapper->foreground_source) - source = wrapper->foreground_source; - - if (wrapper->needs_transform) { + if (wrapper->needs_transform || source_region_id != 0 || mask_region_id != 0) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); @@ -196,10 +199,10 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); - _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id); source = &source_copy.base; - _copy_transformed_pattern (&mask_copy.base, mask, &m); + _copy_transformed_pattern (&mask_copy.base, mask, &m, mask_region_id); mask = &mask_copy.base; } @@ -210,16 +213,17 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, } cairo_status_t -_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; @@ -235,10 +239,7 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (source->is_userfont_foreground && wrapper->foreground_source) - source = wrapper->foreground_source; - - if (wrapper->needs_transform) { + if (wrapper->needs_transform || source_region_id != 0) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); @@ -257,7 +258,7 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); - _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id); source = &source_copy.base; } @@ -275,21 +276,23 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, } cairo_status_t -_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - const cairo_path_fixed_t*path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - const cairo_clip_t *clip) +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + unsigned int fill_region_id, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + const cairo_path_fixed_t *path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + unsigned int stroke_region_id, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path; @@ -306,13 +309,7 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (fill_source->is_userfont_foreground && wrapper->foreground_source) - fill_source = wrapper->foreground_source; - - if (stroke_source->is_userfont_foreground && wrapper->foreground_source) - stroke_source = wrapper->foreground_source; - - if (wrapper->needs_transform) { + if (wrapper->needs_transform || fill_region_id != 0 || stroke_region_id != 0) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); @@ -331,10 +328,10 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); - _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m); + _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m, fill_region_id); stroke_source = &stroke_source_copy.base; - _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m); + _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m, stroke_region_id); fill_source = &fill_source_copy.base; } @@ -356,14 +353,15 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, } cairo_status_t -_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + unsigned int source_region_id, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; @@ -377,10 +375,7 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (source->is_userfont_foreground && wrapper->foreground_source) - source = wrapper->foreground_source; - - if (wrapper->needs_transform) { + if (wrapper->needs_transform || source_region_id != 0) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); @@ -395,7 +390,7 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); - _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id); source = &source_copy.base; } @@ -412,9 +407,10 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, } cairo_status_t -_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, + unsigned int source_region_id, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, @@ -443,10 +439,7 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_surface_get_font_options (wrapper->target, &options); cairo_font_options_merge (&options, &scaled_font->options); - if (source->is_userfont_foreground && wrapper->foreground_source) - source = wrapper->foreground_source; - - if (wrapper->needs_transform) { + if (wrapper->needs_transform || source_region_id != 0) { cairo_matrix_t m; int i; @@ -481,7 +474,7 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); - _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&source_copy.base, source, &m, source_region_id); source = &source_copy.base; } else { if (! cairo_font_options_equal (&options, &scaled_font->options)) { @@ -613,14 +606,6 @@ _cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper, } void -_cairo_surface_wrapper_set_foreground_color (cairo_surface_wrapper_t *wrapper, - const cairo_color_t *color) -{ - if (color) - wrapper->foreground_source = _cairo_pattern_create_solid (color); -} - -void _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, cairo_font_options_t *options) { @@ -651,7 +636,8 @@ _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, wrapper->has_extents = FALSE; wrapper->extents.x = wrapper->extents.y = 0; wrapper->clip = NULL; - wrapper->foreground_source = NULL; + wrapper->source_region_id = 0; + wrapper->mask_region_id = 0; wrapper->needs_transform = FALSE; if (target) { @@ -663,9 +649,6 @@ _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, void _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) { - if (wrapper->foreground_source) - cairo_pattern_destroy (wrapper->foreground_source); - cairo_surface_destroy (wrapper->target); } diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 105f4bff1..f1292e0bb 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -49,7 +49,6 @@ #include "cairo-recording-surface-private.h" #include "cairo-region-private.h" #include "cairo-surface-inline.h" -#include "cairo-tee-surface-private.h" /** * SECTION:cairo-surface @@ -134,7 +133,9 @@ const cairo_surface_t name = { \ CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \ CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \ CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \ - } /* font_options */ \ + }, /* font_options */ \ + NULL, /* foreground_source */ \ + FALSE, /* foreground_used */ \ } /* XXX error object! */ @@ -439,6 +440,9 @@ _cairo_surface_init (cairo_surface_t *surface, surface->snapshot_of = NULL; surface->has_font_options = FALSE; + + surface->foreground_source = NULL; + surface->foreground_used = FALSE; } static void @@ -976,6 +980,9 @@ cairo_surface_destroy (cairo_surface_t *surface) _cairo_user_data_array_fini (&surface->user_data); _cairo_user_data_array_fini (&surface->mime_data); + if (surface->foreground_source) + cairo_pattern_destroy (surface->foreground_source); + if (surface->owns_device) cairo_device_destroy (surface->device); @@ -1774,7 +1781,7 @@ slim_hidden_def (cairo_surface_mark_dirty_rectangle); * by the CTM when drawing to @surface. One common use for this is to * render to very high resolution display devices at a scale factor, so * that code that assumes 1 pixel will be a certain size will still work. - * Setting a transformation via cairo_translate() isn't + * Setting a transformation via cairo_scale() isn't * sufficient to do this, since functions like * cairo_device_to_user() will expose the hidden scale. * @@ -1826,7 +1833,7 @@ slim_hidden_def (cairo_surface_set_device_scale); * @x_scale: the scale in the X direction, in device units * @y_scale: the scale in the Y direction, in device units * - * This function returns the previous device offset set by + * This function returns the previous device scale set by * cairo_surface_set_device_scale(). * * Since: 1.14 @@ -2196,6 +2203,11 @@ _cairo_surface_paint (cairo_surface_t *surface, if (unlikely (status)) return status; + if (source->is_foreground_marker && surface->foreground_source) { + source = surface->foreground_source; + surface->foreground_used = TRUE; + } + status = surface->backend->paint (surface, op, source, clip); is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL; if (status != CAIRO_INT_STATUS_NOTHING_TO_DO || is_clear) { @@ -2246,6 +2258,11 @@ _cairo_surface_mask (cairo_surface_t *surface, if (unlikely (status)) return status; + if (source->is_foreground_marker && surface->foreground_source) { + source = surface->foreground_source; + surface->foreground_used = TRUE; + } + status = surface->backend->mask (surface, op, source, mask, clip); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; @@ -2302,6 +2319,16 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, if (unlikely (status)) return status; + if (fill_source->is_foreground_marker && surface->foreground_source) { + fill_source = surface->foreground_source; + surface->foreground_used = TRUE; + } + + if (stroke_source->is_foreground_marker && surface->foreground_source) { + stroke_source = surface->foreground_source; + surface->foreground_used = TRUE; + } + if (surface->backend->fill_stroke) { cairo_matrix_t dev_ctm = *stroke_ctm; cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; @@ -2376,6 +2403,11 @@ _cairo_surface_stroke (cairo_surface_t *surface, if (unlikely (status)) return status; + if (source->is_foreground_marker && surface->foreground_source) { + source = surface->foreground_source; + surface->foreground_used = TRUE; + } + status = surface->backend->stroke (surface, op, source, path, stroke_style, ctm, ctm_inverse, @@ -2421,6 +2453,11 @@ _cairo_surface_fill (cairo_surface_t *surface, if (unlikely (status)) return status; + if (source->is_foreground_marker && surface->foreground_source) { + source = surface->foreground_source; + surface->foreground_used = TRUE; + } + status = surface->backend->fill (surface, op, source, path, fill_rule, tolerance, antialias, @@ -2719,6 +2756,7 @@ composite_color_glyphs (cairo_surface_t *surface, font_face = cairo_scaled_font_get_font_face (scaled_font); cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); cairo_scaled_font_get_ctm (scaled_font, &ctm); + _cairo_font_options_init_default (&font_options); cairo_scaled_font_get_font_options (scaled_font, &font_options); cairo_matrix_scale (&ctm, x_scale, y_scale); scaled_font = cairo_scaled_font_create (font_face, @@ -2756,9 +2794,21 @@ composite_color_glyphs (cairo_surface_t *surface, goto UNLOCK; if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) != 0) { - skip_cluster = FALSE; - break; - } + cairo_bool_t supports_color_glyph = FALSE; + + if (surface->backend->supports_color_glyph) { + _cairo_scaled_font_thaw_cache (scaled_font); + supports_color_glyph = _cairo_surface_supports_color_glyph (surface, scaled_font, glyphs[gp].index); + + memset (glyph_cache, 0, sizeof (glyph_cache)); + _cairo_scaled_font_freeze_cache (scaled_font); + } + + if (!supports_color_glyph) { + skip_cluster = FALSE; + break; + } + } } if (skip_cluster) { @@ -2894,13 +2944,20 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, if (unlikely (status)) return status; - if (nothing_to_do (surface, op, source)) - return CAIRO_STATUS_SUCCESS; + if (!(_cairo_scaled_font_has_color_glyphs (scaled_font) && + scaled_font->options.color_mode != CAIRO_COLOR_MODE_NO_COLOR)) + { + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; + } status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; + if (source->is_foreground_marker && surface->foreground_source) + source = surface->foreground_source; + if (_cairo_scaled_font_has_color_glyphs (scaled_font) && scaled_font->options.color_mode != CAIRO_COLOR_MODE_NO_COLOR) { @@ -2921,9 +2978,9 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, if (num_glyphs == 0) goto DONE; - } - else + } else { utf8_copy = NULL; + } /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and * show_text_glyphs. Keep in synch. */ @@ -3011,6 +3068,16 @@ _cairo_surface_tag (cairo_surface_t *surface, return _cairo_surface_set_error (surface, status); } +cairo_bool_t +_cairo_surface_supports_color_glyph (cairo_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + if (surface->backend->supports_color_glyph != NULL) + return surface->backend->supports_color_glyph (surface, scaled_font, glyph_index); + + return FALSE; +} /** * _cairo_surface_set_resolution: @@ -3110,6 +3177,7 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_INT_STATUS_DWRITE_ERROR: case CAIRO_STATUS_TAG_ERROR: + case CAIRO_STATUS_SVG_FONT_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; diff --git a/src/cairo-svg-glyph-render.c b/src/cairo-svg-glyph-render.c new file mode 100644 index 000000000..cac8a7a73 --- /dev/null +++ b/src/cairo-svg-glyph-render.c @@ -0,0 +1,3243 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2022 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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-array-private.h" +#include "cairo-ft-private.h" +#include "cairo-pattern-private.h" +#include "cairo-scaled-font-subsets-private.h" + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#if HAVE_FT_SVG_DOCUMENT + +#include <ft2build.h> +#include FT_COLOR_H + +/* #define SVG_RENDER_PRINT_FUNCTIONS 1 */ + +#define WHITE_SPACE_CHARS " \n\r\t\v\f" + +typedef struct { + const char *name; + int red; + int green; + int blue; +} color_name_t; + +/* Must be sorted */ +static color_name_t color_names[] = { + { "aliceblue", 240, 248, 255 }, + { "antiquewhite", 250, 235, 215 }, + { "aqua", 0, 255, 255 }, + { "aquamarine", 127, 255, 212 }, + { "azure", 240, 255, 255 }, + { "beige", 245, 245, 220 }, + { "bisque", 255, 228, 196 }, + { "black", 0, 0, 0 }, + { "blanchedalmond", 255, 235, 205 }, + { "blue", 0, 0, 255 }, + { "blueviolet", 138, 43, 226 }, + { "brown", 165, 42, 42 }, + { "burlywood", 222, 184, 135 }, + { "cadetblue", 95, 158, 160 }, + { "chartreuse", 127, 255, 0 }, + { "chocolate", 210, 105, 30 }, + { "coral", 255, 127, 80 }, + { "cornflowerblue", 100, 149, 237 }, + { "cornsilk", 255, 248, 220 }, + { "crimson", 220, 20, 60 }, + { "cyan", 0, 255, 255 }, + { "darkblue", 0, 0, 139 }, + { "darkcyan", 0, 139, 139 }, + { "darkgoldenrod", 184, 134, 11 }, + { "darkgray", 169, 169, 169 }, + { "darkgreen", 0, 100, 0 }, + { "darkgrey", 169, 169, 169 }, + { "darkkhaki", 189, 183, 107 }, + { "darkmagenta", 139, 0, 139 }, + { "darkolivegreen", 85, 107, 47 }, + { "darkorange", 255, 140, 0 }, + { "darkorchid", 153, 50, 204 }, + { "darkred", 139, 0, 0 }, + { "darksalmon", 233, 150, 122 }, + { "darkseagreen", 143, 188, 143 }, + { "darkslateblue", 72, 61, 139 }, + { "darkslategray", 47, 79, 79 }, + { "darkslategrey", 47, 79, 79 }, + { "darkturquoise", 0, 206, 209 }, + { "darkviolet", 148, 0, 211 }, + { "deeppink", 255, 20, 147 }, + { "deepskyblue", 0, 191, 255 }, + { "dimgray", 105, 105, 105 }, + { "dimgrey", 105, 105, 105 }, + { "dodgerblue", 30, 144, 255 }, + { "firebrick", 178, 34, 34 }, + { "floralwhite", 255, 250, 240 }, + { "forestgreen", 34, 139, 34 }, + { "fuchsia", 255, 0, 255 }, + { "gainsboro", 220, 220, 220 }, + { "ghostwhite", 248, 248, 255 }, + { "gold", 255, 215, 0 }, + { "goldenrod", 218, 165, 32 }, + { "gray", 128, 128, 128 }, + { "green", 0, 128, 0 }, + { "greenyellow", 173, 255, 47 }, + { "grey", 128, 128, 128 }, + { "honeydew", 240, 255, 240 }, + { "hotpink", 255, 105, 180 }, + { "indianred", 205, 92, 92 }, + { "indigo", 75, 0, 130 }, + { "ivory", 255, 255, 240 }, + { "khaki", 240, 230, 140 }, + { "lavender", 230, 230, 250 }, + { "lavenderblush", 255, 240, 245 }, + { "lawngreen", 124, 252, 0 }, + { "lemonchiffon", 255, 250, 205 }, + { "lightblue", 173, 216, 230 }, + { "lightcoral", 240, 128, 128 }, + { "lightcyan", 224, 255, 255 }, + { "lightgoldenrodyellow", 250, 250, 210 }, + { "lightgray", 211, 211, 211 }, + { "lightgreen", 144, 238, 144 }, + { "lightgrey", 211, 211, 211 }, + { "lightpink", 255, 182, 193 }, + { "lightsalmon", 255, 160, 122 }, + { "lightseagreen", 32, 178, 170 }, + { "lightskyblue", 135, 206, 250 }, + { "lightslategray", 119, 136, 153 }, + { "lightslategrey", 119, 136, 153 }, + { "lightsteelblue", 176, 196, 222 }, + { "lightyellow", 255, 255, 224 }, + { "lime", 0, 255, 0 }, + { "limegreen", 50, 205, 50 }, + { "linen", 250, 240, 230 }, + { "magenta", 255, 0, 255 }, + { "maroon", 128, 0, 0 }, + { "mediumaquamarine", 102, 205, 170 }, + { "mediumblue", 0, 0, 205 }, + { "mediumorchid", 186, 85, 211 }, + { "mediumpurple", 147, 112, 219 }, + { "mediumseagreen", 60, 179, 113 }, + { "mediumslateblue", 123, 104, 238 }, + { "mediumspringgreen", 0, 250, 154 }, + { "mediumturquoise", 72, 209, 204 }, + { "mediumvioletred", 199, 21, 133 }, + { "midnightblue", 25, 25, 112 }, + { "mintcream", 245, 255, 250 }, + { "mistyrose", 255, 228, 225 }, + { "moccasin", 255, 228, 181 }, + { "navajowhite", 255, 222, 173 }, + { "navy", 0, 0, 128 }, + { "oldlace", 253, 245, 230 }, + { "olive", 128, 128, 0 }, + { "olivedrab", 107, 142, 35 }, + { "orange", 255, 165, 0 }, + { "orangered", 255, 69, 0 }, + { "orchid", 218, 112, 214 }, + { "palegoldenrod", 238, 232, 170 }, + { "palegreen", 152, 251, 152 }, + { "paleturquoise", 175, 238, 238 }, + { "palevioletred", 219, 112, 147 }, + { "papayawhip", 255, 239, 213 }, + { "peachpuff", 255, 218, 185 }, + { "peru", 205, 133, 63 }, + { "pink", 255, 192, 203 }, + { "plum", 221, 160, 221 }, + { "powderblue", 176, 224, 230 }, + { "purple", 128, 0, 128 }, + { "red", 255, 0, 0 }, + { "rosybrown", 188, 143, 143 }, + { "royalblue", 65, 105, 225 }, + { "saddlebrown", 139, 69, 19 }, + { "salmon", 250, 128, 114 }, + { "sandybrown", 244, 164, 96 }, + { "seagreen", 46, 139, 87 }, + { "seashell", 255, 245, 238 }, + { "sienna", 160, 82, 45 }, + { "silver", 192, 192, 192 }, + { "skyblue", 135, 206, 235 }, + { "slateblue", 106, 90, 205 }, + { "slategray", 112, 128, 144 }, + { "slategrey", 112, 128, 144 }, + { "snow", 255, 250, 250 }, + { "springgreen", 0, 255, 127 }, + { "steelblue", 70, 130, 180 }, + { "tan", 210, 180, 140 }, + { "teal", 0, 128, 128 }, + { "thistle", 216, 191, 216 }, + { "tomato", 255, 99, 71 }, + { "turquoise", 64, 224, 208 }, + { "violet", 238, 130, 238 }, + { "wheat", 245, 222, 179 }, + { "white", 255, 255, 255 }, + { "whitesmoke", 245, 245, 245 }, + { "yellow", 255, 255, 0 }, + { "yellowgreen", 154, 205, 50 } +}; + +typedef struct { + char *name; + char *value; +} svg_attribute_t; + +typedef enum { + CONTAINER_ELEMENT, + EMPTY_ELEMENT, + PROCESSING_INSTRUCTION, + DOCTYPE, + CDATA, + COMMENT +} tag_type_t; + +#define TOP_ELEMENT_TAG "_top" + +typedef struct _cairo_svg_element { + cairo_hash_entry_t base; + tag_type_t type; + char *tag; + char *id; + cairo_array_t attributes; /* svg_attribute_t */ + cairo_array_t children; /* cairo_svg_element_t* */ + cairo_array_t content; /* char */ + cairo_pattern_t *pattern; /* defined if a paint server */ + struct _cairo_svg_element *next; /* next on element stack */ +} cairo_svg_element_t; + +typedef struct _cairo_svg_color { + enum { RGB, FOREGROUND } type; + double red; + double green; + double blue; +} cairo_svg_color_t; + +typedef struct _cairo_svg_paint { + enum { PAINT_COLOR, PAINT_SERVER, PAINT_NONE } type; + cairo_svg_color_t color; + cairo_svg_element_t *paint_server; +} cairo_svg_paint_t; + +typedef enum { + GS_RENDER, + GS_NO_RENDER, + GS_COMPUTE_BBOX, + GS_CLIP +} gs_mode_t; + +typedef struct _cairo_svg_graphics_state { + cairo_svg_paint_t fill; + cairo_svg_paint_t stroke; + cairo_svg_color_t color; + double fill_opacity; + double stroke_opacity; + double opacity; + cairo_fill_rule_t fill_rule; + cairo_fill_rule_t clip_rule; + cairo_path_t *clip_path; + char *dash_array; + double dash_offset; + gs_mode_t mode; + struct { + double x; + double y; + double width; + double height; + } bbox; + struct _cairo_svg_graphics_state *next; +} cairo_svg_graphics_state_t; + +typedef enum { + BUILD_PATTERN_NONE, + BUILD_PATTERN_LINEAR, + BUILD_PATTERN_RADIAL +} build_pattern_t; + +typedef struct _cairo_svg_glyph_render { + cairo_svg_element_t *tree; + cairo_hash_table_t *ids; + cairo_svg_graphics_state_t *graphics_state; + cairo_t *cr; + double units_per_em; + struct { + cairo_svg_element_t *paint_server; + cairo_pattern_t *pattern; + build_pattern_t type; + } build_pattern; + int render_element_tree_depth; + int num_palette_entries; + FT_Color* palette; + + /* Viewport */ + double width; + double height; + cairo_bool_t view_port_set; + + cairo_pattern_t *foreground_marker; + cairo_pattern_t *foreground_source; + cairo_bool_t foreground_source_used; + + int debug; /* 0 = quiet, 1 = errors, 2 = warnings, 3 = info */ +} cairo_svg_glyph_render_t; + + +#define SVG_RENDER_ERROR 1 +#define SVG_RENDER_WARNING 2 +#define SVG_RENDER_INFO 3 + +#define print_error(render, ...) cairo_svg_glyph_render_printf(render, SVG_RENDER_ERROR, ##__VA_ARGS__) +#define print_warning(render, ...) cairo_svg_glyph_render_printf(render, SVG_RENDER_WARNING, ##__VA_ARGS__) +#define print_info(render, ...) cairo_svg_glyph_render_printf(render, SVG_RENDER_INFO, ##__VA_ARGS__) + +static void +cairo_svg_glyph_render_printf (cairo_svg_glyph_render_t *svg_render, + int level, + const char *fmt, ...) CAIRO_PRINTF_FORMAT (3, 4); + +static void +cairo_svg_glyph_render_printf (cairo_svg_glyph_render_t *svg_render, + int level, + const char *fmt, ...) +{ + va_list ap; + + if (svg_render->debug >= level ) { + switch (level) { + case SVG_RENDER_ERROR: + printf("ERROR: "); + break; + case SVG_RENDER_WARNING: + printf("WARNING: "); + break; + } + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + printf ("\n"); + } +} + +static cairo_bool_t +string_equal (const char *s1, const char *s2) +{ + if (s1 && s2) + return strcmp (s1, s2) == 0; + + if (!s1 && !s2) + return TRUE; + + return FALSE; +} + +static cairo_bool_t +string_match (const char **p, const char *str) +{ + if (*p && strncmp (*p, str, strlen (str)) == 0) { + *p += strlen (str); + return TRUE; + } + return FALSE; +} + +static const char * +skip_space (const char *p) +{ + while (*p && _cairo_isspace (*p)) + p++; + + return p; +} + +/* Skip over character c and and whitespace before or after. Returns + * NULL if c not found. */ +static const char * +skip_char (const char *p, char c) +{ + while (_cairo_isspace (*p)) + p++; + + if (*p != c) + return NULL; + + p++; + + while (_cairo_isspace (*p)) + p++; + + return p; +} + +static int +_color_name_compare (const void *a, const void *b) +{ + const color_name_t *a_color = a; + const color_name_t *b_color = b; + + return strcmp (a_color->name, b_color->name); +} + +static void +init_element_id_key (cairo_svg_element_t *element) +{ + element->base.hash = _cairo_hash_string (element->id); +} + +static cairo_bool_t +_element_id_equal (const void *key_a, const void *key_b) +{ + const cairo_svg_element_t *a = key_a; + const cairo_svg_element_t *b = key_b; + + return string_equal (a->id, b->id); +} + +/* Find element with the "id" attribute matching id. id may have the + * '#' prefix. It will be stripped before searching. + */ +static cairo_svg_element_t * +lookup_element (cairo_svg_glyph_render_t *svg_render, const char *id) +{ + cairo_svg_element_t key; + + if (!id || strlen (id) < 1) + return NULL; + + key.id = (char *)(id[0] == '#' ? id + 1 : id); + init_element_id_key (&key); + return _cairo_hash_table_lookup (svg_render->ids, &key.base); +} + +/* Find element with the "id" attribute matching url where url is of + * the form "url(#id)". + */ +static cairo_svg_element_t * +lookup_url_element (cairo_svg_glyph_render_t *svg_render, const char *url) +{ + const char *p = url; + cairo_svg_element_t *element = NULL; + + if (p && string_match (&p, "url")) { + p = skip_char (p, '('); + if (!p) + return NULL; + + const char *end = strpbrk(p, WHITE_SPACE_CHARS ")"); + if (end) { + char *id = _cairo_strndup (p, end - p); + element = lookup_element (svg_render, id); + free (id); + } + } + return element; +} + +static const char * +get_attribute (const cairo_svg_element_t *element, const char *name) +{ + svg_attribute_t attr; + int num_elems, i; + + num_elems = _cairo_array_num_elements (&element->attributes); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&element->attributes, i, &attr); + if (string_equal (attr.name, name)) + return attr.value; + } + return NULL; +} + +static const char * +get_href_attribute (const cairo_svg_element_t *element) +{ + svg_attribute_t attr; + int num_elems, i, len; + + /* SVG2 requires the href attribute to be "href". Older versions + * used "xlink:href". I have seen at least one font that used an + * alternative name space eg "ns1:href". To keep things simple we + * search for an attribute named "href" or ending in ":href". + */ + num_elems = _cairo_array_num_elements (&element->attributes); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&element->attributes, i, &attr); + if (string_equal (attr.name, "href")) + return attr.value; + + len = strlen (attr.name); + if (len > 4 && string_equal (attr.name + len - 5, ":href")) + return attr.value; + } + return NULL; +} + +/* Get a float attribute or float percentage. If attribute is a + * percentage, the returned value is percentage * scale. Does not + * modify value if it returns FALSE. This allows value to be set to a + * default before calling get_float_attribute(), then used without + * checking the return value of this function. + */ +static cairo_bool_t +get_float_or_percent_attribute (const cairo_svg_element_t *element, + const char *name, + double scale, + double *value) +{ + const char *p; + char *end; + double v; + + p = get_attribute (element, name); + if (p) { + v = _cairo_strtod (p, &end); + if (end != p) { + *value = v; + if (*end == '%') + *value *= scale / 100.0; + return TRUE; + } + } + return FALSE; +} + +/* Does not modify value if it returns FALSE. This allows value to be + * set to a default before calling get_float_attribute(), then used + * without checking the return value of this function. + */ +static cairo_bool_t +get_float_attribute (const cairo_svg_element_t *element, const char *name, double *value) +{ + const char *p; + char *end; + double v; + + p = get_attribute (element, name); + if (p) { + v = _cairo_strtod (p, &end); + if (end != p) { + *value = v; + return TRUE; + } + } + return FALSE; +} + +static cairo_fill_rule_t +get_fill_rule_attribute (const cairo_svg_element_t *element, const char *name, cairo_fill_rule_t default_value) +{ + const char *p; + + p = get_attribute (element, name); + if (string_equal (p, "nonzero")) + return CAIRO_FILL_RULE_WINDING; + else if (string_equal (p, "evenodd")) + return CAIRO_FILL_RULE_EVEN_ODD; + else + return default_value; +} + +static void +free_elements (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element) +{ + int num_elems; + + num_elems = _cairo_array_num_elements (&element->children); + for (int i = 0; i < num_elems; i++) { + cairo_svg_element_t *child; + _cairo_array_copy_element (&element->children, i, &child); + free_elements (svg_render, child); + } + _cairo_array_fini (&element->children); + + num_elems = _cairo_array_num_elements (&element->attributes); + for (int i = 0; i < num_elems; i++) { + svg_attribute_t *attr = _cairo_array_index (&element->attributes, i); + free (attr->name); + free (attr->value); + } + _cairo_array_fini (&element->attributes); + _cairo_array_fini (&element->content); + + free (element->tag); + + if (element->id) { + _cairo_hash_table_remove (svg_render->ids, &element->base); + free (element->id); + } + + if (element->pattern) + cairo_pattern_destroy (element->pattern); + + free (element); +} + +#if SVG_RENDER_PRINT_FUNCTIONS + +static void indent(int level) +{ + for (int i = 1; i < level; i++) + printf(" "); +} + +static void +print_element (cairo_svg_element_t *element, cairo_bool_t recurse, int level) +{ + char *content = strndup (_cairo_array_index_const (&element->content, 0), + _cairo_array_num_elements (&element->content)); + + indent(level); + if (element->type == COMMENT) { + printf("<!--%s-->\n", content); + } else if (element->type == CDATA) { + printf("<![CDATA[%s]]>\n", content); + } else if (element->type == DOCTYPE) { + printf("<!DOCTYPE%s>\n", content); + } else if (element->type == PROCESSING_INSTRUCTION) { + printf("<?%s?>\n", content); + } else { + cairo_bool_t top_element = string_equal (element->tag, TOP_ELEMENT_TAG); + + if (!top_element) { + printf("<%s", element->tag); + int num_elems = _cairo_array_num_elements (&element->attributes); + for (int i = 0; i < num_elems; i++) { + svg_attribute_t *attr = _cairo_array_index (&element->attributes, i); + printf(" %s=\"%s\"", attr->name, attr->value); + } + if (num_elems > 0) + printf(" "); + + if (element->type == EMPTY_ELEMENT) + printf("/>\n"); + else + printf(">\n"); + } + + if (element->type == CONTAINER_ELEMENT) { + if (recurse) { + int num_elems = _cairo_array_num_elements (&element->children); + for (int i = 0; i < num_elems; i++) { + cairo_svg_element_t *child; + _cairo_array_copy_element (&element->children, i, &child); + print_element (child, TRUE, level + 1); + } + } + if (!top_element) + printf("</%s>\n", element->tag); + } + } + free (content); +} +#endif + +static const char * +parse_list_of_floats (const char *p, + int num_required, + int num_optional, + cairo_bool_t *have_optional, + va_list ap) +{ + double d; + double *dp; + char *end; + const char *q = NULL; + int num_found = 0; + + for (int i = 0; i < num_required + num_optional; i++) { + while (p && (*p == ',' || _cairo_isspace (*p))) + p++; + + if (!p) + break; + + d = _cairo_strtod (p, &end); + if (end == p) { + p = NULL; + break; + } + p = end; + dp = va_arg (ap, double *); + *dp = d; + num_found++; + if (num_found == num_required) + q = p; + } + + if (num_optional > 0) { + if (num_found == num_required + num_optional) { + *have_optional = TRUE; + } else { + *have_optional = FALSE; + /* restore pointer to end of required floats */ + p = q; + } + } + + return p; +} + +static const char * +get_floats (const char *p, + int num_required, + int num_optional, + cairo_bool_t *have_optional, + ...) +{ + va_list ap; + + va_start (ap, have_optional); + p = parse_list_of_floats (p, num_required, num_optional, have_optional, ap); + va_end (ap); + return p; +} + +static const char * +get_path_params (const char *p, int num_params, ...) +{ + va_list ap; + + va_start (ap, num_params); + p = parse_list_of_floats (p, num_params, 0, NULL, ap); + va_end (ap); + return p; +} + +static cairo_bool_t +get_color (cairo_svg_glyph_render_t *svg_render, + const char *s, + cairo_svg_color_t *color) +{ + int len, matched; + unsigned r = 0, g = 0, b = 0; + + if (!s) + return FALSE; + + len = strlen(s); + + if (string_equal (s, "inherit")) { + return FALSE; + } else if (string_equal (s, "currentColor") || + string_equal (s, "context-fill") || + string_equal (s, "context-stroke")) + { + *color = svg_render->graphics_state->color; + return TRUE; + } else if (len > 0 && s[0] == '#') { + if (len == 4) { + matched = sscanf (s + 1, "%1x%1x%1x", &r, &g, &b); + if (matched == 3) { + /* Each digit is repeated to convert to 6 digits. eg 0x123 -> 0x112233 */ + color->type = RGB; + color->red = 0x11*r/255.0; + color->green = 0x11*g/255.0; + color->blue = 0x11*b/255.0; + return TRUE; + } + } else if (len == 7) { + matched = sscanf (s + 1, "%2x%2x%2x", &r, &g, &b); + if (matched == 3) { + color->type = RGB; + color->red = r/255.0; + color->green = g/255.0; + color->blue = b/255.0; + return TRUE; + } + } + } else if (strncmp (s, "rgb", 3) == 0) { + matched = sscanf (s, "rgb ( %u , %u , %u )", &r, &g, &b); + if (matched == 3) { + color->type = RGB; + color->red = r/255.0; + color->green = g/255.0; + color->blue = b/255.0; + return TRUE; + } + } else if (strncmp (s, "var", 3) == 0) { + /* CPAL palettes colors. eg "var(--color0, yellow)" */ + s += 3; + s = skip_char (s, '('); + if (!string_match (&s, "--color")) + return FALSE; + + char *end; + int entry = strtol (s, &end, 10); + if (end == s) + return FALSE; + + if (svg_render->palette && entry >= 0 && entry < svg_render->num_palette_entries) { + FT_Color *palette_color = &svg_render->palette[entry]; + color->type = RGB; + color->red = palette_color->red / 255.0; + color->green = palette_color->green/ 255.0; + color->blue = palette_color->blue / 255.0; + return TRUE; + } else { + /* Fallback color */ + s = skip_char (end, ','); + if (!s) + return FALSE; + + end = strpbrk(s, WHITE_SPACE_CHARS ")"); + if (!end || end == s) + return FALSE; + + char *fallback = _cairo_strndup (s, end - s); + cairo_bool_t success = get_color (svg_render, fallback, color); + free (fallback); + return success; + } + } else { + const color_name_t *color_name; + color_name_t color_name_key; + + color_name_key.name = (char *) s; + color_name = bsearch (&color_name_key, + color_names, + ARRAY_LENGTH (color_names), + sizeof (color_name_t), + _color_name_compare); + if (color_name) { + color->type = RGB; + color->red = color_name->red/255.0; + color->green = color_name->green/255.0; + color->blue = color_name->blue/255.0; + return TRUE; + } + } + return FALSE; +} + +static void +get_paint (cairo_svg_glyph_render_t *svg_render, + const char *p, + cairo_svg_paint_t *paint) +{ + cairo_svg_element_t *element; + + if (string_match (&p, "none")) { + paint->type = PAINT_NONE; + paint->paint_server = NULL; + } else if (p && strncmp (p, "url", 3) == 0) { + element = lookup_url_element (svg_render, p); + if (element) { + paint->type = PAINT_SERVER; + paint->paint_server = element; + } + } else { + if (get_color (svg_render, p, &paint->color)) { + paint->type = PAINT_COLOR; + paint->paint_server = NULL; + } + } +} + +#ifdef SVG_RENDER_PRINT_FUNCTIONS + +static void +print_color (cairo_svg_color_t *color) +{ + switch (color->type) { + case FOREGROUND_COLOR: + printf("foreground"); + break; + case RGB: + printf("#%02x%02x%02x", + (int)(color->red*255), + (int)(color->red*255), + (int)(color->red*255)); + break; + } +} + +static void +print_paint (cairo_svg_paint_t *paint) +{ + printf("Paint: "); + switch (paint->type) { + case PAINT_COLOR: + printf("color: "); + print_color (&paint->color); + break; + case PAINT_SERVER: + printf("server: %s", paint->paint_server->tag); + break; + case PAINT_NONE: + printf("none"); + break; + } + printf("\n"); +} + +#endif + +static void +parse_error (cairo_svg_glyph_render_t *svg_render, + const char *string, + const char *location, + const char *fmt, + ...) CAIRO_PRINTF_FORMAT (4, 5); + +static void +parse_error (cairo_svg_glyph_render_t *svg_render, + const char *string, + const char *location, + const char *fmt, + ...) +{ + va_list ap; + const int context = 40; + const char *start; + const char *end; + + if (svg_render->debug >= SVG_RENDER_ERROR) { + printf("ERROR: "); + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + putchar ('\n'); + start = location - context; + if (start < string) + start = string; + + end = location + strlen (location); + if (end - location > context) + end = location + context; + + for (const char *p = start; p < end; p++) { + if (_cairo_isspace (*p)) + putchar (' '); + else + putchar (*p); + } + putchar ('\n'); + + for (int i = 0; i < location - start; i++) + putchar(' '); + putchar ('^'); + putchar ('\n'); + printf (" at position %td\n", location - string); + } +} + +static cairo_bool_t +append_attribute (cairo_svg_element_t *element, svg_attribute_t *attribute) +{ + const char *p; + const char *end; + svg_attribute_t attr; + + memset (&attr, 0, sizeof (attr)); + if (string_equal (attribute->name, "style")) { + /* split style into individual attributes */ + p = attribute->value; + while (*p) { + end = strchr (p, ':'); + if (!end || end == p) + break; + attr.name = _cairo_strndup (p, end - p); + p = end + 1; + p = skip_space(p); + end = strchr (p, ';'); + if (!end) + end = strchr (p, 0); + if (end == p) + goto split_style_fail; + + attr.value = _cairo_strndup (p, end - p); + if (*end) + p = end + 1; + + if (_cairo_array_append (&element->attributes, &attr)) + goto split_style_fail; + + memset (&attr, 0, sizeof (attr)); + p = skip_space (p); + } + } + + if (_cairo_array_append (&element->attributes, attribute)) + return FALSE; + + return TRUE; + + split_style_fail: + free (attr.name); + free (attr.value); + return FALSE; +} + +static cairo_bool_t +add_child_element (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *parent, + cairo_svg_element_t *child) +{ + cairo_status_t status; + const char* id; + + id = get_attribute (child, "id"); + if (id) { + child->id = strdup (id); + init_element_id_key (child); + status = _cairo_hash_table_insert (svg_render->ids, &child->base); + if (unlikely (status)) + return FALSE; + } + + status = _cairo_array_append (&parent->children, &child); + return status == CAIRO_STATUS_SUCCESS; +} + +static cairo_svg_element_t * +create_element (tag_type_t type, char *tag) +{ + cairo_svg_element_t *elem; + cairo_status_t status; + + elem = _cairo_malloc (sizeof (cairo_svg_element_t)); + if (unlikely (elem == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + elem->type = type; + elem->tag = tag; + elem->id = NULL; + _cairo_array_init (&elem->attributes, sizeof(svg_attribute_t)); + _cairo_array_init (&elem->children, sizeof(cairo_svg_element_t *)); + _cairo_array_init (&elem->content, sizeof(char)); + elem->pattern = NULL; + elem->next = NULL; + + return elem; +} + +static const char * +parse_attributes (cairo_svg_glyph_render_t *svg_render, + const char *attributes, + cairo_svg_element_t *element) +{ + svg_attribute_t attr; + char quote_char; + const char *p; + const char *end; + + p = attributes; + memset (&attr, 0, sizeof (svg_attribute_t)); + p = skip_space (p); + while (*p && *p != '/' && *p != '>' && *p != '?') { + end = strpbrk(p, WHITE_SPACE_CHARS "="); + if (!end) { + parse_error (svg_render, attributes, p, "Could not find '='"); + goto fail; + } + + if (end == p) { + parse_error (svg_render, attributes, p, "Missing attribute name"); + goto fail; + } + + attr.name = _cairo_strndup (p, end - p); + p = end; + + p = skip_space (p); + if (*p != '=') { + parse_error (svg_render, attributes, p, "Expected '='"); + goto fail; + } + + p++; + p = skip_space (p); + if (*p == '\"' || *p == '\'') { + quote_char = *p; + } else { + parse_error (svg_render, attributes, p, "Could not find '\"' or '''"); + goto fail; + } + + p++; + end = strchr (p, quote_char); + if (!end) { + parse_error (svg_render, attributes, p, "Could not find '%c'", quote_char); + goto fail; + } + + attr.value = _cairo_strndup (p, end - p); + p = end + 1; + + if (!append_attribute (element, &attr)) + goto fail; + + memset (&attr, 0, sizeof (svg_attribute_t)); + + p = skip_space (p); + } + + return p; + + fail: + free (attr.name); + free (attr.value); + return NULL; +} + +static cairo_bool_t +parse_svg (cairo_svg_glyph_render_t *svg_render, + const char *svg_document) +{ + const char *p = svg_document; + const char *end; + int nesting; /* when > 0 we parse content */ + cairo_svg_element_t *open_elem; /* Stack of open elements */ + cairo_svg_element_t *new_elem = NULL; + char *name; + cairo_status_t status; + + /* Create top level element to use as a container for all top + * level elements in the document and push it on the stack. */ + open_elem = create_element (CONTAINER_ELEMENT, strdup(TOP_ELEMENT_TAG)); + + /* We don't want to add content to the top level container. There + * should only be whitesapce between tags. */ + nesting = 0; + + while (*p) { + if (nesting > 0) { + /* In an open element. Anything before the next '<' is content */ + end = strchr (p, '<'); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find '<'"); + goto fail; + } + status = _cairo_array_append_multiple (&open_elem->content, p, end - p); + p = end; + + } else { + p = skip_space (p); + if (*p == 0) + break; /* end of document */ + } + + /* We should now be at the start of a tag */ + if (*p != '<') { + parse_error (svg_render, svg_document, p, "Could not find '<'"); + goto fail; + } + + p++; + if (*p == '!') { + p++; + if (string_match (&p, "[CDATA[")) { + new_elem = create_element (CDATA, NULL); + end = strstr (p, "]]>"); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find ']]>'"); + goto fail; + } + + status = _cairo_array_append_multiple (&new_elem->content, p, end - p); + p = end + 3; + } else if (string_match (&p, "--")) { + new_elem = create_element (COMMENT, NULL); + end = strstr (p, "-->"); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find '-->'"); + goto fail; + } + + status = _cairo_array_append_multiple (&new_elem->content, p, end - p); + p = end + 3; + } else if (string_match (&p, "DOCTYPE")) { + new_elem = create_element (DOCTYPE, NULL); + end = strchr (p, '>'); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find '>'"); + goto fail; + } + + status = _cairo_array_append_multiple (&new_elem->content, p, end - p); + p = end + 1; + } else { + parse_error (svg_render, svg_document, p, "Invalid"); + goto fail; + } + + if (!add_child_element (svg_render, open_elem, new_elem)) + goto fail; + + new_elem = NULL; + continue; + } + + if (*p == '?') { + p++; + new_elem = create_element (PROCESSING_INSTRUCTION, NULL); + end = strstr (p, "?>"); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find '?>'"); + goto fail; + } + + status = _cairo_array_append_multiple (&new_elem->content, p, end - p); + p = end + 2; + + if (!add_child_element (svg_render, open_elem, new_elem)) + goto fail; + + new_elem = NULL; + continue; + } + + if (*p == '/') { + /* Closing tag */ + p++; + + /* find end of tag name */ + end = strpbrk(p, WHITE_SPACE_CHARS ">"); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find '>'"); + goto fail; + } + + name = _cairo_strndup (p, end - p); + p = end; + p = skip_space (p); + if (*p != '>') { + parse_error (svg_render, svg_document, p, "Could not find '>'"); + free (name); + goto fail; + } + + p++; + if (nesting == 0) { + parse_error (svg_render, svg_document, p, "parse_elements: parsed </%s> but no matching start tag", name); + free (name); + goto fail; + } + if (!string_equal (name, open_elem->tag)) { + parse_error (svg_render, svg_document, p, + "parse_elements: found </%s> but current open tag is <%s>", + name, open_elem->tag); + free (name); + goto fail; + } + + /* pop top element on open elements stack into new_elem */ + new_elem = open_elem; + open_elem = open_elem->next; + new_elem->next = NULL; + nesting--; + + free (name); + if (!add_child_element (svg_render, open_elem, new_elem)) + goto fail; + + new_elem = NULL; + continue; + } + + /* We should now be in a start or empty element tag */ + + /* find end of tag name */ + end = strpbrk(p, WHITE_SPACE_CHARS "/>"); + if (!end) { + parse_error (svg_render, svg_document, p, "Could not find '>'"); + goto fail; + } + + name = _cairo_strndup (p, end - p); + p = end; + + new_elem = create_element (CONTAINER_ELEMENT, name); + p = parse_attributes (svg_render, p, new_elem); + if (!p) + goto fail; + + p = skip_space (p); + if (*p == '/') { + new_elem->type = EMPTY_ELEMENT; + p++; + } + + if (!p || *p != '>') { + print_error (svg_render, "Could not find '>'"); + goto fail; + } + + p++; + if (new_elem->type == EMPTY_ELEMENT) { + if (!add_child_element (svg_render, open_elem, new_elem)) + goto fail; + + new_elem = NULL; + } else { + /* push new elem onto open elements stack */ + new_elem->next = open_elem; + open_elem = new_elem; + new_elem = NULL; + nesting++; + } + } + + if (nesting != 0) { + parse_error (svg_render, svg_document, p, "Missing closing tag for <%s>", open_elem->tag); + goto fail; + } + + svg_render->tree = open_elem; + return TRUE; + + fail: + if (new_elem) + free_elements (svg_render, new_elem); + + while (open_elem) { + cairo_svg_element_t *elem = open_elem; + open_elem = open_elem->next; + free_elements (svg_render, elem); + } + + return FALSE; +} + +static cairo_bool_t +parse_transform (const char *p, cairo_matrix_t *matrix) +{ + cairo_matrix_t m; + double x, y, a; + cairo_bool_t have_optional; + + cairo_matrix_init_identity (matrix); + while (p) { + while (p && (*p == ',' || _cairo_isspace (*p))) + p++; + + if (!p || *p == 0) + break; + + if (string_match (&p, "matrix")) { + p = skip_char (p, '('); + if (!p) + break; + + p = get_floats (p, 6, 0, NULL, &m.xx, &m.yx, &m.xy, &m.yy, &m.x0, &m.y0); + if (!p) + break; + + p = skip_char (p, ')'); + if (!p) + break; + + cairo_matrix_multiply (matrix, &m, matrix); + + } else if (string_match (&p, "translate")) { + p = skip_char (p, '('); + if (!p) + break; + + p = get_floats (p, 1, 1, &have_optional, &x, &y); + if (!p) + break; + + p = skip_char (p, ')'); + if (!p) + break; + + if (!have_optional) + y = 0; + + cairo_matrix_translate (matrix, x, y); + + } else if (string_match (&p, "scale")) { + p = skip_char (p, '('); + if (!p) + break; + + p = get_floats (p, 1, 1, &have_optional, &x, &y); + if (!p) + break; + + p = skip_char (p, ')'); + if (!p) + break; + + if (!have_optional) + y = x; + + cairo_matrix_scale (matrix, x, y); + + } else if (string_match (&p, "rotate")) { + p = skip_char (p, '('); + if (!p) + break; + + p = get_floats (p, 1, 2, &have_optional, &a, &x, &y); + if (!p) + break; + + p = skip_char (p, ')'); + if (!p) + break; + + if (!have_optional) { + x = 0; + y = 0; + } + + a *= M_PI/180.0; + cairo_matrix_translate (matrix, x, y); + cairo_matrix_rotate (matrix, a); + cairo_matrix_translate (matrix, -x, -y); + + } else if (string_match (&p, "skewX")) { + p = skip_char (p, '('); + if (!p) + break; + + p = get_floats (p, 1, 0, NULL, &a); + if (!p) + break; + + p = skip_char (p, ')'); + if (!p) + break; + + a *= M_PI/180.0; + cairo_matrix_init_identity (&m); + m.xy = tan (a); + cairo_matrix_multiply (matrix, &m, matrix); + + } else if (string_match (&p, "skewY")) { + p = skip_char (p, '('); + if (!p) + break; + + p = get_floats (p, 1, 0, NULL, &a); + if (!p) + break; + + p = skip_char (p, ')'); + if (!p) + break; + + a *= M_PI/180.0; + cairo_matrix_init_identity (&m); + m.yx = tan (a); + cairo_matrix_multiply (matrix, &m, matrix); + + } else { + break; + } + } + return p != NULL; +} + +static void +render_element_tree (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_svg_element_t *display_element, + cairo_bool_t children_only); + +static cairo_pattern_t * +create_pattern (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *paint_server) +{ + cairo_pattern_t *pattern = NULL; + + if (paint_server) { + svg_render->build_pattern.paint_server = paint_server; + render_element_tree (svg_render, paint_server, NULL, FALSE); + pattern = svg_render->build_pattern.pattern; + svg_render->build_pattern.pattern = NULL; + svg_render->build_pattern.paint_server = NULL; + svg_render->build_pattern.type = BUILD_PATTERN_NONE; + } + + if (!pattern) + pattern = cairo_pattern_create_rgb (0, 0, 0); + + return pattern; +} + +static cairo_bool_t +render_element_svg (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double width, height; + double vb_x, vb_y, vb_height, vb_width; + const char *p; + const char *end; + + if (end_tag) + return FALSE; + + /* Default viewport width, height is EM square */ + if (!get_float_or_percent_attribute (element, "width", svg_render->units_per_em, &width)) + width = svg_render->units_per_em; + + if (!get_float_or_percent_attribute (element, "height", svg_render->units_per_em, &height)) + height = svg_render->units_per_em; + + /* Transform viewport to unit square, centering it if width != height. */ + if (width > height) { + cairo_scale (svg_render->cr, 1.0/width, 1.0/width); + cairo_translate (svg_render->cr, 0, (width - height)/2.0); + } else { + cairo_scale (svg_render->cr, 1.0/height, 1.0/height); + cairo_translate (svg_render->cr, (height - width)/2.0, 0); + } + + svg_render->width = width; + svg_render->height = height; + + p = get_attribute (element, "viewBox"); + if (p) { + /* Transform viewport to viewbox */ + end = get_path_params (p, 4, &vb_x, &vb_y, &vb_width, &vb_height); + if (!end) { + print_warning (svg_render, "viewBox expected 4 numbers: %s", p); + return FALSE; + } + cairo_translate (svg_render->cr, -vb_x * width/vb_width, -vb_y * width/vb_width); + cairo_scale (svg_render->cr, width/vb_width, height/vb_height); + svg_render->width = vb_width; + svg_render->height = vb_height; + } + + svg_render->view_port_set = TRUE; + return TRUE; +} + +static cairo_bool_t +render_element_clip_path (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + cairo_svg_graphics_state_t *gs = svg_render->graphics_state; + const char *p; + + if (end_tag || gs->mode != GS_CLIP || svg_render->build_pattern.type != BUILD_PATTERN_NONE) { + return FALSE; + } + + p = get_attribute (element, "clipPathUnits"); + if (string_equal (p, "objectBoundingBox")) { + cairo_translate (svg_render->cr, + svg_render->graphics_state->bbox.x, + svg_render->graphics_state->bbox.y); + cairo_scale (svg_render->cr, + svg_render->graphics_state->bbox.width, + svg_render->graphics_state->bbox.height); + } + + return TRUE; +} + +static void +apply_gradient_attributes (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element) +{ + cairo_pattern_t *pattern = svg_render->build_pattern.pattern; + cairo_bool_t object_bbox = TRUE; + cairo_matrix_t transform; + cairo_matrix_t mat; + const char *p; + + if (!pattern) + return; + + p = get_attribute (element, "gradientUnits"); + if (string_equal (p, "userSpaceOnUse")) + object_bbox = FALSE; + + cairo_matrix_init_identity (&mat); + if (object_bbox) { + cairo_matrix_translate (&mat, + svg_render->graphics_state->bbox.x, + svg_render->graphics_state->bbox.y); + cairo_matrix_scale (&mat, + svg_render->graphics_state->bbox.width, + svg_render->graphics_state->bbox.height); + } + + p = get_attribute (element, "gradientTransform"); + if (parse_transform (p, &transform)) + cairo_matrix_multiply (&mat, &transform, &mat); + + if (cairo_matrix_invert (&mat) == CAIRO_STATUS_SUCCESS) + cairo_pattern_set_matrix (pattern, &mat); + + p = get_attribute (element, "spreadMethod"); + if (string_equal (p, "reflect")) + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT); + else if (string_equal (p, "repeat")) + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); +} + +static cairo_bool_t +render_element_linear_gradient (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double x1, y1, x2, y2; + + if (svg_render->build_pattern.paint_server != element || + end_tag || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + /* FIXME default value for userSpaceOnUse? */ + double width = 1.0; + double height = 1.0; + + if (!get_float_or_percent_attribute (element, "x1", width, &x1)) + x1 = 0.0; + + if (!get_float_or_percent_attribute (element, "y1", height, &y1)) + y1 = 0.0; + + if (!get_float_or_percent_attribute (element, "x2", width, &x2)) + x2 = width; + + if (!get_float_or_percent_attribute (element, "y2", height, &y2)) + y2 = 0.0; + + if (svg_render->build_pattern.pattern) + abort(); + + svg_render->build_pattern.pattern = cairo_pattern_create_linear (x1, y1, x2, y2); + svg_render->build_pattern.type = BUILD_PATTERN_LINEAR; + apply_gradient_attributes (svg_render, element); + return TRUE; +} + +static cairo_bool_t +render_element_radial_gradient (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double cx, cy, r, fx, fy; + + if (svg_render->build_pattern.paint_server != element || + end_tag || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + /* FIXME default value for userSpaceOnUse? */ + double width = 1.0; + double height = 1.0; + + if (!get_float_or_percent_attribute (element, "cx", width, &cx)) + cx = 0.5 * width; + + if (!get_float_or_percent_attribute (element, "cy", height, &cy)) + cy = 0.5 * height; + + if (!get_float_or_percent_attribute (element, "r", width, &r)) + r = 0.5 * width; + + if (!get_float_or_percent_attribute (element, "fx", width, &fx)) + fx = cx; + + if (!get_float_or_percent_attribute (element, "fy", height, &fy)) + fy = cy; + + svg_render->build_pattern.pattern = cairo_pattern_create_radial (fx, fy, 0, cx, cy, r); + svg_render->build_pattern.type = BUILD_PATTERN_RADIAL; + apply_gradient_attributes (svg_render, element); + return TRUE; +} + +static cairo_bool_t +render_element_stop (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double offset, opacity; + cairo_pattern_t *pattern = svg_render->build_pattern.pattern; + + if (!pattern) + return FALSE; + + if (cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_LINEAR && + cairo_pattern_get_type (pattern) != CAIRO_PATTERN_TYPE_RADIAL) + return FALSE; + + if (!get_float_or_percent_attribute (element, "offset", 1.0, &offset)) + return FALSE; + + if (!get_float_attribute (element, "stop-opacity", &opacity)) + opacity = 1.0; + + cairo_svg_color_t color; + get_color (svg_render, "black", &color); + get_color (svg_render, get_attribute(element, "stop-color"), &color); + if (color.type == RGB) { + cairo_pattern_add_color_stop_rgba (pattern, + offset, + color.red, + color.green, + color.blue, + opacity); + } else { /* color.type == FOREGROUND */ + double red, green, blue, alpha; + if (cairo_pattern_get_rgba (svg_render->foreground_source, &red, &green, &blue, &alpha) == CAIRO_STATUS_SUCCESS) { + svg_render->foreground_source_used = TRUE; + } else { + red = green = blue = 0; + alpha = 1; + } + cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, alpha); + } + return TRUE; +} + +static cairo_bool_t +render_element_g (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + if (svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + if (!end_tag) { + cairo_push_group (svg_render->cr); + } else { + cairo_pop_group_to_source (svg_render->cr); + cairo_paint_with_alpha (svg_render->cr, svg_render->graphics_state->opacity); + } + return TRUE; +} + +typedef struct { + const char *data; /* current position in base64 data */ + char buf[3]; /* decode buffer */ + int buf_pos; /* current position in buf_pos. */ +} base64_decode_t; + +static cairo_status_t +_read_png_from_base64 (void *closure, unsigned char *data, unsigned int length) +{ + base64_decode_t *decode = closure; + int n, c; + unsigned val; + + while (length) { + if (decode->buf_pos >= 0) { + *data++ = decode->buf[decode->buf_pos++]; + length--; + if (decode->buf_pos == 3) + decode->buf_pos = -1; + } + if (length > 0 && decode->buf_pos < 0) { + n = 0; + while (*decode->data && n < 4) { + c = *decode->data++; + if (c >='A' && c <='Z') { + val = (val << 6) | (c -'A'); + n++; + } else if (c >='a' && c <='z') { + val = (val << 6) | (c -'a' + 26); + n++; + } else if (c >='0' && c <='9') { + val = (val << 6) | (c -'0' + 52); + n++; + } else if (c =='+') { + val = (val << 6) | 62; + n++; + } else if (c =='/') { + val = (val << 6) | 63; + n++; + } else if (c == '=') { + val = (val << 6); + n++; + } + } + if (n < 4) + return CAIRO_STATUS_READ_ERROR; + + decode->buf[0] = val >> 16; + decode->buf[1] = val >> 8; + decode->buf[2] = val >> 0; + decode->buf_pos = 0; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +render_element_image (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double x, y, width, height; + int w, h; + const char *data; + cairo_surface_t *surface; + base64_decode_t decode; + + if (svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + if (!get_float_attribute (element, "x", &x)) + x = 0; + + if (!get_float_attribute (element, "y", &y)) + y = 0; + + if (!get_float_attribute (element, "width", &width)) + return FALSE; + + if (!get_float_attribute (element, "height", &height)) + return FALSE; + + data = get_href_attribute (element); + if (!data) + return FALSE; + + if (!string_match (&data, "data:image/png;base64,")) + return FALSE; + + decode.data = data; + decode.buf_pos = -1; + surface = cairo_image_surface_create_from_png_stream (_read_png_from_base64, &decode); + if (cairo_surface_status (surface)) { + print_warning (svg_render, "Unable to decode PNG"); + cairo_surface_destroy (surface); + return FALSE; + } + + w = cairo_image_surface_get_width (surface); + h = cairo_image_surface_get_height (surface); + + if (w > 0 && h > 0) { + cairo_translate (svg_render->cr, x, y); + cairo_scale (svg_render->cr, width/w, height/h); + cairo_set_source_surface (svg_render->cr, surface, 0, 0); + cairo_paint (svg_render->cr); + } + + cairo_surface_destroy (surface); + + return FALSE; +} + +static cairo_bool_t +render_element_use (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double x = 0; + double y = 0; + const char *id; + + if (end_tag || svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + get_float_attribute (element, "x", &x); + get_float_attribute (element, "y", &y); + + id = get_href_attribute (element); + if (!id) + return FALSE; + + cairo_svg_element_t *use_element = lookup_element (svg_render, id); + cairo_translate (svg_render->cr, x, y); + render_element_tree (svg_render, use_element, NULL, FALSE); + return TRUE; +} + +static cairo_bool_t +draw_path (cairo_svg_glyph_render_t *svg_render) +{ + cairo_svg_graphics_state_t *gs = svg_render->graphics_state; + cairo_pattern_t *pattern; + cairo_bool_t opacity_group = FALSE; + + if (gs->mode == GS_COMPUTE_BBOX) { + cairo_set_source_rgb (svg_render->cr, 0, 0, 0); + cairo_set_fill_rule (svg_render->cr, gs->fill_rule); + cairo_fill (svg_render->cr); + return FALSE; + } else if (gs->mode == GS_CLIP) { + return FALSE; + } + + if (gs->opacity < 1.0) { + cairo_push_group (svg_render->cr); + opacity_group = TRUE; + } + + cairo_path_t *path = cairo_copy_path (svg_render->cr); + cairo_new_path (svg_render->cr); + + if (gs->fill.type != PAINT_NONE) { + cairo_bool_t group = FALSE; + if (gs->fill.type == PAINT_COLOR) { + if (gs->fill.color.type == RGB) { + cairo_set_source_rgba (svg_render->cr, + gs->fill.color.red, + gs->fill.color.green, + gs->fill.color.blue, + gs->fill_opacity); + } else if (gs->fill.color.type == FOREGROUND) { + cairo_set_source (svg_render->cr, svg_render->foreground_marker); + if (gs->fill_opacity < 1.0) + group = TRUE; + } + } else if (gs->fill.type == PAINT_SERVER) { + pattern = create_pattern (svg_render, gs->fill.paint_server); + cairo_set_source (svg_render->cr, pattern); + cairo_pattern_destroy (pattern); + if (gs->fill_opacity < 1.0) + group = TRUE; + } + + if (group) + cairo_push_group (svg_render->cr); + + cairo_append_path (svg_render->cr, path); + cairo_set_fill_rule (svg_render->cr, gs->fill_rule); + cairo_fill (svg_render->cr); + if (group) { + cairo_pop_group_to_source (svg_render->cr); + cairo_paint_with_alpha (svg_render->cr, gs->fill_opacity); + } + } + + if (gs->stroke.type != PAINT_NONE) { + cairo_bool_t group = FALSE; + if (gs->stroke.type == PAINT_COLOR) { + if (gs->stroke.color.type == RGB) { + cairo_set_source_rgba (svg_render->cr, + gs->stroke.color.red, + gs->stroke.color.green, + gs->stroke.color.blue, + gs->stroke_opacity); + } else if (gs->fill.color.type == FOREGROUND) { + cairo_set_source (svg_render->cr, svg_render->foreground_marker); + if (gs->fill_opacity < 1.0) + group = TRUE; + } + } else if (gs->stroke.type == PAINT_SERVER) { + pattern = create_pattern (svg_render, gs->stroke.paint_server); + cairo_set_source (svg_render->cr, pattern); + cairo_pattern_destroy (pattern); + if (gs->stroke_opacity < 1.0) + group = TRUE; + } + + if (group) + cairo_push_group (svg_render->cr); + + cairo_append_path (svg_render->cr, path); + cairo_stroke (svg_render->cr); + + if (group) { + cairo_pop_group_to_source (svg_render->cr); + cairo_paint_with_alpha (svg_render->cr, gs->stroke_opacity); + } + } + + cairo_path_destroy (path); + + if (opacity_group) { + cairo_pop_group_to_source (svg_render->cr); + cairo_paint_with_alpha (svg_render->cr, gs->opacity); + } + return TRUE; +} + +static void +elliptical_arc (cairo_svg_glyph_render_t *svg_render, + double cx, + double cy, + double rx, + double ry, + double angle1, + double angle2) +{ + cairo_save (svg_render->cr); + cairo_translate (svg_render->cr, cx, cy); + cairo_scale (svg_render->cr, rx, ry); + cairo_arc (svg_render->cr, 0, 0, 1, angle1, angle2); + cairo_restore (svg_render->cr); +} + +static cairo_bool_t +render_element_rect (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double x = 0; + double y = 0; + double width = svg_render->width; + double height = svg_render->height; + double rx = 0; + double ry = 0; + + if (end_tag || + svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + get_float_or_percent_attribute (element, "x", svg_render->width, &x); + get_float_or_percent_attribute (element, "y", svg_render->height, &y); + get_float_or_percent_attribute (element, "width", svg_render->width, &width); + get_float_or_percent_attribute (element, "height", svg_render->height, &height); + get_float_or_percent_attribute (element, "rx", svg_render->width, &rx); + get_float_or_percent_attribute (element, "ry", svg_render->height, &ry); + + if (rx == 0 && ry == 0) { + cairo_rectangle (svg_render->cr, x, y, width, height); + } else { + cairo_move_to (svg_render->cr, x + rx, y); + cairo_line_to (svg_render->cr, x + width - rx, y); + elliptical_arc (svg_render, x + width - rx, y + ry, rx, ry, -M_PI/2, 0); + cairo_line_to (svg_render->cr, x + width, y + height - ry); + elliptical_arc (svg_render, x + width - rx, y + height - ry, rx, ry, 0, M_PI/2); + cairo_line_to (svg_render->cr, x + rx, y + height); + elliptical_arc (svg_render, x + rx, y + height - ry, rx, ry, M_PI/2, M_PI); + cairo_line_to (svg_render->cr, x, y + ry); + elliptical_arc (svg_render, x + rx, y + ry, rx, ry, M_PI, -M_PI/2); + } + + draw_path (svg_render); + return TRUE; +} + +static cairo_bool_t +render_element_circle (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double cx = 0; + double cy = 0; + double r = 0; + + if (end_tag || + svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + get_float_or_percent_attribute (element, "cx", svg_render->width, &cx); + get_float_or_percent_attribute (element, "cy", svg_render->height, &cy); + get_float_or_percent_attribute (element, "r", svg_render->width, &r); + + cairo_arc (svg_render->cr, cx, cy, r, 0, 2*M_PI); + + draw_path (svg_render); + return TRUE; +} + +static cairo_bool_t +render_element_ellipse (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double cx = 0; + double cy = 0; + double rx = 0; + double ry = 0; + + if (end_tag || + svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + get_float_or_percent_attribute (element, "cx", svg_render->width, &cx); + get_float_or_percent_attribute (element, "cy", svg_render->height, &cy); + get_float_or_percent_attribute (element, "rx", svg_render->width, &rx); + get_float_or_percent_attribute (element, "ry", svg_render->height, &ry); + + elliptical_arc (svg_render, cx, cy, rx, ry, 0, 2*M_PI); + draw_path (svg_render); + return TRUE; +} + +static cairo_bool_t +render_element_line (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double x1 = 0; + double y1 = 0; + double x2 = 0; + double y2 = 0; + + if (end_tag || + svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + get_float_or_percent_attribute (element, "x1", svg_render->width, &x1); + get_float_or_percent_attribute (element, "y1", svg_render->height, &y1); + get_float_or_percent_attribute (element, "x2", svg_render->width, &x2); + get_float_or_percent_attribute (element, "y2", svg_render->height, &y2); + + cairo_move_to (svg_render->cr, x1, y1); + cairo_line_to (svg_render->cr, x2, y2); + + draw_path (svg_render); + return TRUE; +} + +static cairo_bool_t +render_element_polyline (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + const char *p; + const char *end; + double x, y; + cairo_bool_t have_move = FALSE; + + if (end_tag || + svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + p = get_attribute (element, "points"); + do { + end = get_path_params (p, 2, &x, &y); + if (!end) { + print_warning (svg_render, "points expected 2 numbers: %s", p); + break; + } + p = end; + if (!have_move) { + cairo_move_to (svg_render->cr, x, y); + have_move = TRUE; + } else { + cairo_line_to (svg_render->cr, x, y); + } + p = skip_space (p); + } while (p && *p); + + if (string_equal (element->tag, "polygon")) + cairo_close_path (svg_render->cr); + + draw_path (svg_render); + return TRUE; +} + +static double +angle_between_vectors (double ux, + double uy, + double vx, + double vy) +{ + double dot = ux*vx + uy*vy; + double umag = sqrt (ux*ux + uy*uy); + double vmag = sqrt (vx*vx + vy*vy); + double c = dot/(umag*vmag); + if (c > 1.0) + c = 1.0; + + if (c < -1.0) + c = -1.0; + + double a = acos (c); + if (ux * vy - uy * vx < 0.0) + a = -a; + + return a; +} + +static void +arc_path (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double rx, double ry, + double rotate, + cairo_bool_t large_flag, + cairo_bool_t sweep_flag) +{ + double x1_, y1_, cx_, cy_; + double xm, ym, cx, cy; + double a, b, d; + double ux, uy, vx, vy; + double theta, delta_theta; + double epsilon; + cairo_matrix_t ctm; + + cairo_get_matrix (cr, &ctm); + epsilon = _cairo_matrix_transformed_circle_major_axis (&ctm, cairo_get_tolerance (cr)); + + rotate *= M_PI/180.0; + + /* Convert endpoint to center parameterization. + * See SVG 1.1 Appendix F.6. Step numbers are the steps in the appendix. + */ + + rx = fabs (rx); + ry = fabs (ry); + if (rx < epsilon || ry < epsilon) { + cairo_line_to (cr, x2, y2); + return; + } + + if (fabs(x1 - x2) < epsilon && fabs(y1 - y2) < epsilon) { + cairo_line_to (cr, x2, y2); + return; + } + + /* Step 1 */ + xm = (x1 - x2)/2; + ym = (y1 - y2)/2; + x1_ = xm * cos (rotate) + ym * sin (rotate); + y1_ = xm * -sin (rotate) + ym * cos (rotate); + + d = (x1_*x1_)/(rx*rx) + (y1_*y1_)/(ry*ry); + if (d > 1.0) { + d = sqrt (d); + rx *= d; + ry *= d; + } + + /* Step 2 */ + a = (rx*rx * y1_*y1_) + (ry*ry * x1_*x1_); + if (a == 0.0) + return; + + b = (rx*rx * ry*ry) / a - 1.0; + if (b < 0) + b = 0.0; + + d = sqrt(b); + if (large_flag == sweep_flag) + d = -d; + + cx_ = d * rx*y1_/ry; + cy_ = d * -ry*x1_/rx; + + /* Step 3 */ + cx = cx_ * cos (rotate) - cy_ * sin (rotate) + (x1 + x2)/2; + cy = cx_ * sin (rotate) + cy_ * cos (rotate) + (y1 + y2)/2; + + /* Step 4 */ + ux = (x1_ - cx_)/rx; + uy = (y1_ - cy_)/ry; + vx = (-x1_ - cx_)/rx; + vy = (-y1_ - cy_)/ry; + theta = angle_between_vectors (1.0, 0, ux, uy); + delta_theta = angle_between_vectors (ux, uy, vx, vy); + + if (!sweep_flag && delta_theta > 0) + delta_theta -= 2 * M_PI; + else if (sweep_flag && delta_theta < 0) + delta_theta += 2 * M_PI; + + /* Now we can call cairo_arc() */ + cairo_save (cr); + cairo_translate (cr, cx, cy); + cairo_scale (cr, rx, ry); + cairo_rotate (cr, theta); + if (delta_theta >= 0.0) + cairo_arc (cr, 0, 0, 1, 0, delta_theta); + else + cairo_arc_negative (cr, 0, 0, 1, 0, delta_theta); + cairo_restore (cr); +} + +static void +get_current_point (cairo_svg_glyph_render_t *svg_render, double *x, double *y) +{ + if (cairo_has_current_point (svg_render->cr)) { + cairo_get_current_point (svg_render->cr, x, y); + } else { + *x = 0; + *y = 0; + } +} + +static void +reflect_point (double origin_x, double origin_y, double *x, double *y) +{ + *x = 2*origin_x - *x; + *y = 2*origin_y - *y; +} + +static cairo_bool_t +render_element_path (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + double cur_x, cur_y; + double last_cp_x, last_cp_y; + double x, y, x1, y1, x2, y2; + double qx1, qy1, qx2, qy2; + double rx, ry, rotate, large_flag, sweep_flag; + cairo_bool_t rel, have_move; + enum { CUBIC, QUADRATIC, OTHER } last_op; + + if (end_tag || + svg_render->graphics_state->mode == GS_NO_RENDER || + svg_render->build_pattern.type != BUILD_PATTERN_NONE) + return FALSE; + + last_op = OTHER; + const char *p = get_attribute (element, "d"); + const char *end; + int op; + + while (p) { + while (p && _cairo_isspace (*p)) + p++; + + if (!p || *p == 0) + break; + + op = *p; + switch (op) { + case 'M': + case 'm': + rel = op == 'm'; + p++; + have_move = FALSE; + do { + end = get_path_params (p, 2, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 2 numbers: %s", op, p); + break; + } + p = end; + if (rel) { + get_current_point (svg_render, &cur_x, &cur_y); + x += cur_x; + y += cur_y; + } + if (!have_move) { + cairo_move_to (svg_render->cr, x, y); + have_move = TRUE; + } else { + cairo_line_to (svg_render->cr, x, y); + } + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = OTHER; + break; + case 'Z': + case 'z': + p++; + cairo_close_path (svg_render->cr); + last_op = OTHER; + break; + case 'L': + case 'l': + rel = op == 'l'; + p++; + do { + end = get_path_params (p, 2, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 2 numbers: %s", op, p); + break; + } + p = end; + if (rel) { + get_current_point (svg_render, &cur_x, &cur_y); + x += cur_x; + y += cur_y; + } + cairo_line_to (svg_render->cr, x, y); + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = OTHER; + break; + case 'H': + case 'h': + rel = op == 'h'; + p++; + do { + end = get_path_params (p, 1, &x1); + if (!end) { + print_warning (svg_render, "path %c expected a number: %s", op, p); + break; + } + p = end; + get_current_point (svg_render, &cur_x, &cur_y); + if (rel) { + x1 += cur_x; + } + cairo_line_to (svg_render->cr, x1, cur_y); + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = OTHER; + break; + case 'V': + case 'v': + rel = op == 'v'; + p++; + do { + end = get_path_params (p, 1, &y1); + if (!end) { + print_warning (svg_render, "path %c expected a number: %s", op, p); + break; + } + p = end; + get_current_point (svg_render, &cur_x, &cur_y); + if (rel) { + y1 += cur_y; + } + cairo_line_to (svg_render->cr, cur_x, y1); + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = OTHER; + break; + case 'C': + case 'c': + rel = op == 'c'; + p++; + do { + end = get_path_params (p, 6, &x1, &y1, &x2, &y2, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 6 numbers: %s", op, p); + break; + } + p = end; + if (rel) { + get_current_point (svg_render, &cur_x, &cur_y); + x1 += cur_x; + y1 += cur_y; + x2 += cur_x; + y2 += cur_y; + x += cur_x; + y += cur_y; + } + cairo_curve_to (svg_render->cr, x1, y1, x2, y2, x, y); + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = CUBIC; + last_cp_x = x2; + last_cp_y = y2; + break; + case 'S': + case 's': + rel = op == 's'; + p++; + do { + end = get_path_params (p, 4, &x2, &y2, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 4 numbers: %s", op, p); + break; + } + p = end; + get_current_point (svg_render, &cur_x, &cur_y); + if (rel) { + x2 += cur_x; + y2 += cur_y; + x += cur_x; + y += cur_y; + } + if (last_op == CUBIC) { + x1 = last_cp_x; + y1 = last_cp_y; + reflect_point (cur_x, cur_y, &x1, &y1); + } else { + x1 = cur_x; + y1 = cur_y; + } + cairo_curve_to (svg_render->cr, x1, y1, x2, y2, x, y); + last_op = CUBIC; + last_cp_x = x2; + last_cp_y = y2; + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + break; + case 'Q': + case 'q': + rel = op == 'q'; + p++; + do { + end = get_path_params (p, 4, &x1, &y1, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 4 numbers: %s", op, p); + break; + } + p = end; + get_current_point (svg_render, &cur_x, &cur_y); + if (rel) { + x1 += cur_x; + y1 += cur_y; + x += cur_x; + y += cur_y; + } + qx1 = cur_x + (x1 - cur_x)*2/3; + qy1 = cur_y + (y1 - cur_y)*2/3; + qx2 = x + (x1 - x)*2/3; + qy2 = y + (y1 - y)*2/3; + cairo_curve_to (svg_render->cr, qx1, qy1, qx2, qy2, x, y); + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = QUADRATIC; + last_cp_x = x1; + last_cp_y = y1; + break; + case 'T': + case 't': + rel = op == 't'; + p++; + do { + end = get_path_params (p, 2, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 2 numbers: %s", op, p); + break; + } + p = end; + get_current_point (svg_render, &cur_x, &cur_y); + if (rel) { + x += cur_x; + y += cur_y; + } + if (last_op == QUADRATIC) { + x1 = last_cp_x; + y1 = last_cp_y; + reflect_point (cur_x, cur_y, &x1, &y1); + } else { + x1 = cur_x; + y1 = cur_y; + } + qx1 = cur_x + (x1 - cur_x)*2/3; + qy1 = cur_y + (y1 - cur_y)*2/3; + qx2 = x + (x1 - x)*2/3; + qy2 = y + (y1 - y)*2/3; + cairo_curve_to (svg_render->cr, qx1, qy1, qx2, qy2, x, y); + last_op = QUADRATIC; + last_cp_x = x1; + last_cp_y = y1; + p = skip_space (p); + } while (p && *p && *p && !_cairo_isalpha(*p)); + break; + case 'A': + case 'a': + rel = op == 'a'; + p++; + do { + end = get_path_params (p, 7, &rx, &ry, &rotate, &large_flag, &sweep_flag, &x, &y); + if (!end) { + print_warning (svg_render, "path %c expected 7 numbers: %s", op, p); + break; + } + p = end; + get_current_point (svg_render, &cur_x, &cur_y); + if (rel) { + x += cur_x; + y += cur_y; + } + arc_path (svg_render->cr, + cur_x, cur_y, + x, y, + rx, ry, + rotate, + large_flag > 0.5, + sweep_flag > 0.5); + p = skip_space (p); + } while (p && *p && !_cairo_isalpha(*p)); + last_op = OTHER; + break; + default: + p = NULL; + break; + } + } + + draw_path (svg_render); + return TRUE; +} + +static void +init_graphics_state (cairo_svg_glyph_render_t *svg_render) +{ + cairo_svg_graphics_state_t *gs; + + gs = _cairo_malloc (sizeof (cairo_svg_graphics_state_t)); + get_paint (svg_render, "black", &gs->fill); + get_paint (svg_render, "none", &gs->stroke); + gs->color.type = FOREGROUND; + gs->fill_opacity = 1.0; + gs->stroke_opacity = 1.0; + gs->opacity = 1.0; + gs->fill_rule = CAIRO_FILL_RULE_WINDING; + gs->clip_rule = CAIRO_FILL_RULE_WINDING; + gs->clip_path = NULL; + gs->dash_array = NULL; + gs->dash_offset = 0.0; + gs->mode = GS_RENDER; + gs->bbox.x = 0; + gs->bbox.y = 0; + gs->bbox.width = 0; + gs->bbox.height = 0; + gs->next = NULL; + + svg_render->graphics_state = gs; + + cairo_save (svg_render->cr); + cairo_set_source_rgb (svg_render->cr, 0, 0, 0); + cairo_set_line_width (svg_render->cr, 1.0); + cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_BUTT); + cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_MITER); + cairo_set_miter_limit (svg_render->cr, 4.0); +} + +#define MAX_DASHES 100 +static void update_dash (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element) +{ + cairo_svg_graphics_state_t *gs = svg_render->graphics_state; + const char *p; + char *end; + double value; + double dash_array[MAX_DASHES]; + int num_dashes = 0; + cairo_bool_t not_zero = FALSE; + + if (gs->dash_array == NULL || string_equal (gs->dash_array, "none")) { + cairo_set_dash (svg_render->cr, NULL, 0, 0); + return; + } + + p = gs->dash_array; + while (*p && num_dashes < MAX_DASHES) { + while (*p && (*p == ',' || _cairo_isspace (*p))) + p++; + + if (*p == 0) + break; + + value = _cairo_strtod (p, &end); + if (end == p) + break; + + p = end; + if (*p == '%') { + value *= svg_render->width / 100.0; + p++; + } + + if (value < 0.0) + return; + + if (value > 0.0) + not_zero = TRUE; + + dash_array[num_dashes++] = value; + } + + if (not_zero) + cairo_set_dash (svg_render->cr, dash_array, num_dashes, gs->dash_offset); +} + +static cairo_bool_t +pattern_requires_bbox (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *paint_server) +{ + const char *p; + + if (string_equal (paint_server->tag, "linearGradient") || + string_equal (paint_server->tag, "radialGradient")) + { + p = get_attribute (paint_server, "gradientUnits"); + if (string_equal (p, "userSpaceOnUse")) + return FALSE; + + return TRUE; + } + return FALSE; +} + +static cairo_bool_t +clip_requires_bbox (cairo_svg_glyph_render_t *svg_render, + const char *clip_path) +{ + cairo_svg_element_t *element; + const char *p; + + if (clip_path && strncmp (clip_path, "url", 3) == 0) { + element = lookup_url_element (svg_render, clip_path); + if (element) { + p = get_attribute (element, "clipPathUnits"); + if (string_equal (p, "objectBoundingBox")) + return TRUE; + } + } + return FALSE; +} + +static cairo_bool_t +need_bbox (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element) +{ + cairo_svg_graphics_state_t *gs = svg_render->graphics_state; + cairo_bool_t fill_needs_bbox = FALSE; + cairo_bool_t stroke_needs_bbox = FALSE; + cairo_bool_t clip_needs_bbox = FALSE; + + if (gs->mode != GS_RENDER) + return FALSE; + + if (gs->fill.type == PAINT_SERVER && pattern_requires_bbox (svg_render, gs->fill.paint_server)) + fill_needs_bbox = TRUE; + + if (gs->stroke.type == PAINT_SERVER && pattern_requires_bbox (svg_render, gs->stroke.paint_server)) + stroke_needs_bbox = TRUE; + + if (clip_requires_bbox (svg_render, get_attribute (element, "clip-path"))) + clip_needs_bbox = TRUE; + + if (string_equal (element->tag, "circle") || + string_equal (element->tag, "ellipse") || + string_equal (element->tag, "path") || + string_equal (element->tag, "polygon") || + string_equal (element->tag, "rect")) + { + return fill_needs_bbox || stroke_needs_bbox || clip_needs_bbox; + } + + if (string_equal (element->tag, "line") || + string_equal (element->tag, "polyline")) + { + return stroke_needs_bbox || clip_needs_bbox; + } + + if (string_equal (element->tag, "g") || + string_equal (element->tag, "image") || + string_equal (element->tag, "use")) + { + return clip_needs_bbox; + } + + return FALSE; +} + +static cairo_bool_t +call_element (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag); + +static void +update_graphics_state (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element) +{ + double value; + const char *p; + cairo_svg_graphics_state_t *gs = svg_render->graphics_state; + + p = get_attribute (element, "transform"); + if (p) { + cairo_matrix_t m; + if (parse_transform (p, &m)) + cairo_transform (svg_render->cr, &m); + } + + /* The transform is all we need for bbox computation. The SVG spec + * excludes clipping and stroke-width from the bbox. */ + if (gs->mode == GS_COMPUTE_BBOX) + return; + + p = get_attribute (element, "color"); + if (p) + get_color (svg_render, p, &gs->color); + + if (!get_float_attribute (element, "opacity", &gs->opacity)) + gs->opacity = 1.0; + + p = get_attribute (element, "fill"); + if (p) { + get_paint (svg_render, p, &gs->fill); + } + + get_float_attribute (element, "fill-opacity", &gs->fill_opacity); + + gs->fill_rule = get_fill_rule_attribute (element, "fill-rule", gs->fill_rule); + + gs->clip_rule = get_fill_rule_attribute (element, "fill-rule", gs->clip_rule); + + p = get_attribute (element, "stroke"); + if (p) + get_paint (svg_render, p, &gs->stroke); + + if (get_float_or_percent_attribute (element, "stroke-width", svg_render->width, &value)) + cairo_set_line_width (svg_render->cr, value); + + p = get_attribute (element, "stroke-linecap"); + if (string_equal (p, "butt")) + cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_BUTT); + else if (string_equal (p, "round")) + cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_ROUND); + else if (string_equal (p, "square")) + cairo_set_line_cap (svg_render->cr, CAIRO_LINE_CAP_SQUARE); + + p = get_attribute (element, "stroke-linejoin"); + if (string_equal (p, "miter")) + cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_MITER); + else if (string_equal (p, "round")) + cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_ROUND); + else if (string_equal (p, "bevel")) + cairo_set_line_join (svg_render->cr, CAIRO_LINE_JOIN_BEVEL); + + if (get_float_attribute (element, "stroke-miterlimit", &value)) + cairo_set_miter_limit (svg_render->cr, value); + + p = get_attribute (element, "stroke-dasharray"); + if (p) { + free (gs->dash_array); + gs->dash_array = strdup (p); + } + + get_float_or_percent_attribute (element, "stroke-dashoffset", svg_render->width, &gs->dash_offset); + update_dash (svg_render, element); + + /* Some elements may need the bounding box of the element thay are + * applied to. As this recursively calls render_element on the + * same element while we are in render_element and setting up the + * graphics state, we check gs->mode to avoid re-entering the + * compute bbox code. The GS_COMPUTE_MODE flag is also used by + * render functions to ignore patterns and strokes (SVG spec + * ignores stroke with in bbox calculations) and just use a solid + * color. + */ + if (gs->mode == GS_RENDER && need_bbox (svg_render, element)) { + cairo_surface_t *recording = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *old_cr = svg_render->cr; + svg_render->cr = cairo_create (recording); + gs_mode_t old_mode = gs->mode; + gs->mode = GS_COMPUTE_BBOX; + /* To avoid recursing back into this function, we call the + * element directory then use render_element_tree to render + * the children */ + call_element (svg_render, element, FALSE); + render_element_tree (svg_render, element, NULL, TRUE); + if (element->type == CONTAINER_ELEMENT) + call_element (svg_render, element, TRUE); + gs->mode = old_mode; + cairo_destroy (svg_render->cr); + svg_render->cr = old_cr; + cairo_recording_surface_ink_extents (recording, + &gs->bbox.x, + &gs->bbox.y, + &gs->bbox.width, + &gs->bbox.height); + cairo_surface_destroy (recording); + } + + /* clip-path may require bbox */ + p = get_attribute (element, "clip-path"); + if (p && strncmp (p, "url", 3) == 0) { + element = lookup_url_element (svg_render, p); + if (element) { + gs_mode_t old_mode = gs->mode; + gs->mode = GS_CLIP; + render_element_tree (svg_render, element, NULL, FALSE); + cairo_set_fill_rule (svg_render->cr, gs->clip_rule); + cairo_clip (svg_render->cr); + gs->mode = old_mode; + } + } +} + +static void +save_graphics_state (cairo_svg_glyph_render_t *svg_render) +{ + cairo_svg_graphics_state_t *gs; + + cairo_save (svg_render->cr); + + gs = _cairo_malloc (sizeof (cairo_svg_graphics_state_t)); + gs->fill = svg_render->graphics_state->fill; + gs->stroke = svg_render->graphics_state->stroke; + gs->color = svg_render->graphics_state->color; + gs->fill_opacity = svg_render->graphics_state->fill_opacity; + gs->stroke_opacity = svg_render->graphics_state->stroke_opacity; + gs->opacity = svg_render->graphics_state->opacity; + gs->fill_rule = svg_render->graphics_state->fill_rule; + gs->clip_rule = svg_render->graphics_state->clip_rule; + gs->clip_path = NULL; + gs->dash_array = NULL; + if (svg_render->graphics_state->dash_array) + gs->dash_array = strdup (svg_render->graphics_state->dash_array); + gs->dash_offset = svg_render->graphics_state->dash_offset; + gs->mode = svg_render->graphics_state->mode; + gs->bbox = svg_render->graphics_state->bbox; + gs->next = svg_render->graphics_state; + svg_render->graphics_state = gs; +} + +static void +restore_graphics_state (cairo_svg_glyph_render_t *svg_render) +{ + cairo_svg_graphics_state_t *gs; + + gs = svg_render->graphics_state; + svg_render->graphics_state = gs->next; + if (gs->clip_path) + cairo_path_destroy (gs->clip_path); + free (gs->dash_array); + free (gs); + + cairo_restore (svg_render->cr); +} + +/* render function returns TRUE if render_element_tree() is to render + * the child nodes, FALSE if render_element_tree() is to skip the + * child nodes. + */ +struct render_func { + const char *tag; + cairo_bool_t (*render) (cairo_svg_glyph_render_t *, cairo_svg_element_t *, cairo_bool_t); +}; + +/* Must be sorted */ +static const struct render_func render_funcs[] = { + { "circle", render_element_circle }, + { "clipPath", render_element_clip_path }, + { "defs", NULL }, + { "desc", NULL }, + { "ellipse", render_element_ellipse }, + { "g", render_element_g }, + { "image", render_element_image }, + { "line", render_element_line }, + { "linearGradient", render_element_linear_gradient }, + { "metadata", NULL }, + { "path", render_element_path }, + { "polygon", render_element_polyline }, + { "polyline", render_element_polyline }, + { "radialGradient", render_element_radial_gradient }, + { "rect", render_element_rect }, + { "stop", render_element_stop }, + { "svg", render_element_svg }, + { "title", NULL }, + { "use", render_element_use }, +}; + +static int +_render_func_compare (const void *a, const void *b) +{ + const struct render_func *render_func_a = a; + const struct render_func *render_func_b = b; + + return strcmp (render_func_a->tag, render_func_b->tag); +} + +static cairo_bool_t +call_element (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag) +{ + const struct render_func *func; + struct render_func key; + cairo_bool_t recurse = FALSE; + + key.tag = element->tag; + key.render = NULL; + func = bsearch (&key, + render_funcs, + ARRAY_LENGTH (render_funcs), + sizeof (struct render_func), + _render_func_compare); + if (func) { + if (func->render) { + recurse = func->render (svg_render, element, end_tag); + } + } else { + print_warning (svg_render, "Unsupported element: %s", element->tag); + } + + return recurse; +} + +static cairo_bool_t +render_element (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_bool_t end_tag, + cairo_svg_element_t *display_element) +{ + cairo_bool_t recurse = FALSE; + cairo_svg_graphics_state_t *gs; + + /* Ignore elements if we have not seen "<svg>". Ignore + * "<svg>" if we have seen it */ + if (svg_render->view_port_set) { + if (string_equal (element->tag, "svg")) + return FALSE; + } else { + if (!string_equal (element->tag, "svg")) + return FALSE; + } + + if (element->type == EMPTY_ELEMENT || + (element->type == CONTAINER_ELEMENT && !end_tag)) + { + save_graphics_state (svg_render); + update_graphics_state (svg_render, element); + } + + gs = svg_render->graphics_state; + if (gs->mode == GS_NO_RENDER && element == display_element) + gs->mode = GS_RENDER; + + recurse = call_element (svg_render, element, end_tag); + + if (element->type == EMPTY_ELEMENT || + (element->type == CONTAINER_ELEMENT && end_tag)) + { + restore_graphics_state (svg_render); + } + + return recurse; +} + +#define MAX_DEPTH 100 + +static void +render_element_tree (cairo_svg_glyph_render_t *svg_render, + cairo_svg_element_t *element, + cairo_svg_element_t *display_element, + cairo_bool_t children_only) +{ + if (!element) + return; + + /* Avoid circular references by limiting the number of recursive + * calls to this function. */ + if (svg_render->render_element_tree_depth > MAX_DEPTH) + return; + + svg_render->render_element_tree_depth++; + if (element->type == EMPTY_ELEMENT && !children_only) { + render_element (svg_render, element, FALSE, display_element); + + } else if (element->type == CONTAINER_ELEMENT) { + int num_elems; + cairo_bool_t recurse = TRUE;; + + if (!children_only) + recurse = render_element (svg_render, element, FALSE, display_element); + + /* We only render the children if the parent returned + * success. This is how we avoid rendering non display + * elements like gradients, <defs>, and anything not + * implemented. */ + if (recurse) { + num_elems = _cairo_array_num_elements (&element->children); + for (int i = 0; i < num_elems; i++) { + cairo_svg_element_t *child; + _cairo_array_copy_element (&element->children, i, &child); + render_element_tree (svg_render, child, display_element, FALSE); + } + } + + if (!children_only) + render_element (svg_render, element, TRUE, display_element); + } + svg_render->render_element_tree_depth--; +} + +static void +render_element_tree_id (cairo_svg_glyph_render_t *svg_render, + const char *element_id) +{ + cairo_svg_element_t *glyph_element = NULL; + + if (element_id) + glyph_element = lookup_element (svg_render, element_id); + + if (glyph_element) + svg_render->graphics_state->mode = GS_NO_RENDER; + else + svg_render->graphics_state->mode = GS_RENDER; + + render_element_tree (svg_render, svg_render->tree, glyph_element, TRUE); +} + +cairo_status_t +_cairo_render_svg_glyph (const char *svg_document, + unsigned long first_glyph, + unsigned long last_glyph, + unsigned long glyph, + double units_per_em, + FT_Color *palette, + int num_palette_entries, + cairo_t *cr, + cairo_pattern_t *foreground_source, + cairo_bool_t *foreground_source_used) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + cairo_svg_glyph_render_t *svg_render = _cairo_malloc (sizeof (cairo_svg_glyph_render_t)); + if (unlikely (svg_render == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + svg_render->tree = NULL; + svg_render->ids = _cairo_hash_table_create (_element_id_equal); + if (unlikely (svg_render->ids == NULL)) { + free (svg_render); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + svg_render->debug = 0; + const char *s = getenv ("CAIRO_DEBUG_SVG_RENDER"); + if (s) { + if (strlen (s) > 0) + svg_render->debug = atoi (s); + else + svg_render->debug = SVG_RENDER_ERROR; + } + + svg_render->cr = cr; + svg_render->units_per_em = units_per_em; + svg_render->build_pattern.paint_server = NULL; + svg_render->build_pattern.pattern = NULL; + svg_render->build_pattern.type = BUILD_PATTERN_NONE; + svg_render->render_element_tree_depth = 0; + svg_render->view_port_set = FALSE; + svg_render->num_palette_entries = num_palette_entries; + svg_render->palette = palette; + + svg_render->foreground_marker = _cairo_pattern_create_foreground_marker (); + svg_render->foreground_source = cairo_pattern_reference (foreground_source);; + svg_render->foreground_source_used = FALSE; + + init_graphics_state (svg_render); + + print_info (svg_render, "Glyph ID: %ld", glyph); + print_info (svg_render, "Palette Entries: %d", num_palette_entries); + print_info (svg_render, "Units per EM: %f", units_per_em); + print_info (svg_render, "SVG Document:\n%s\n", svg_document); + + /* First parse elements into a tree and populate ids hash table */ + if (!parse_svg (svg_render, svg_document)) { + print_error (svg_render, "Parse SVG document failed"); + status = CAIRO_STATUS_SVG_FONT_ERROR; + goto cleanup; + } + +#if SVG_RENDER_PRINT_FUNCTIONS + printf("\nTREE\n"); + if (svg_render->tree) { + print_element (svg_render->tree, TRUE, 0); + printf("\n"); + } +#endif + + /* Next, render glyph */ + if (first_glyph == last_glyph) { + /* Render whole document */ + render_element_tree_id (svg_render, NULL); + } else { + /* Render element with id "glyphID" where ID is glyph number. */ + + char glyph_id[30]; + snprintf(glyph_id, sizeof(glyph_id), "#glyph%ld", glyph); + render_element_tree_id (svg_render, glyph_id); + } + + cleanup: + if (svg_render->build_pattern.pattern) + cairo_pattern_destroy (svg_render->build_pattern.pattern); + + if (svg_render->tree) + free_elements (svg_render, svg_render->tree); + + while (svg_render->graphics_state) + restore_graphics_state (svg_render); + + cairo_pattern_destroy (svg_render->foreground_marker); + cairo_pattern_destroy (svg_render->foreground_source); + *foreground_source_used = svg_render->foreground_source_used; + + /* The hash entry for each element with an id is removed by + * free_elements() */ + _cairo_hash_table_destroy (svg_render->ids); + + free (svg_render); + + return status; +} + +#ifdef DEBUG_SVG_RENDER + +/** + * _cairo_debug_svg_render: + * + * Debug function for cairo-svg-glyph-render.c. Allows invoking the renderer from outside + * cairo to test with SVG documents, and to facilitate comparison with librsvg rendering. + * The viewport is . + * + * @cr: render target + * @svg_document: SVG Document + * @element: element within svg_document to render (eg "#glyph8"), or NULL to render entire document. + * @debug_level: 0 - quiet, 1 - print errors, 2 - print warnings, 3 - info + * @return TRUE on success, ie no errors, FALSE if error + **/ +cairo_bool_t +_cairo_debug_svg_render (cairo_t *cr, + const char *svg_document, + const char *element, + double units_per_em, + int debug_level); + +cairo_bool_t +_cairo_debug_svg_render (cairo_t *cr, + const char *svg_document, + const char *element, + double units_per_em, + int debug_level) +{ + cairo_status_t status; + cairo_bool_t foreground_source_used; + cairo_pattern_t *foreground = _cairo_pattern_create_foreground_marker (); + + status = _cairo_render_svg_glyph (svg_document, + 1, 1, 1, + units_per_em, + NULL, 0, + cr, + foreground, + &foreground_source_used); + cairo_pattern_destroy (foreground); + + return status == CAIRO_STATUS_SUCCESS; +} + +#endif /* DEBUG_SVG_RENDER */ + +#endif /* HAVE_FT_SVG_DOCUMENT */ diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index b39a94a37..b7212a547 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -1555,14 +1555,6 @@ _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 (unlikely (status)) - goto FAIL; - - status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, - _cairo_svg_document_emit_font_subset, - document); - - FAIL: _cairo_scaled_font_subsets_destroy (document->font_subsets); document->font_subsets = NULL; diff --git a/src/cairo-svg.h b/src/cairo-svg.h index 5328cb583..7745eec90 100644 --- a/src/cairo-svg.h +++ b/src/cairo-svg.h @@ -73,9 +73,9 @@ typedef enum _cairo_svg_version { * lengths in the SVG specification. * * See also: - * https://www.w3.org/TR/SVG/coords.html#Units - * https://www.w3.org/TR/SVG/types.html#DataTypeLength - * https://www.w3.org/TR/css-values-3/#lengths + * - [SVG Units](https://www.w3.org/TR/SVG/coords.html#Units) + * - [SVG Types](https://www.w3.org/TR/SVG/types.html#DataTypeLength) + * - [CSS Length](https://www.w3.org/TR/css-values-3/#lengths) * * Since: 1.16 **/ diff --git a/src/cairo-tag-attributes.c b/src/cairo-tag-attributes.c index 6d0f63f11..85467ad73 100644 --- a/src/cairo-tag-attributes.c +++ b/src/cairo-tag-attributes.c @@ -366,12 +366,9 @@ parse_name (const char *attributes, const char *p, const char **end, char **s) p2++; len = p2 - p; - name = _cairo_malloc (len + 1); + name = _cairo_strndup (p, len); if (unlikely (name == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (name, p, len); - name[len] = 0; *s = name; *end = p2; diff --git a/src/cairo-tee-surface-private.h b/src/cairo-tee-surface-private.h deleted file mode 100644 index a83cfc959..000000000 --- a/src/cairo-tee-surface-private.h +++ /dev/null @@ -1,47 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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): - * Chris Wilson <chris@chris-wilson.co.uk> - */ - -#ifndef CAIRO_TEE_SURFACE_PRIVATE_H -#define CAIRO_TEE_SURFACE_PRIVATE_H - -#include "cairoint.h" - -cairo_private cairo_surface_t * -_cairo_tee_surface_find_match (void *abstract_surface, - const cairo_surface_backend_t *backend, - cairo_content_t content); - -#endif /* CAIRO_TEE_SURFACE_PRIVATE_H */ diff --git a/src/cairo-tee-surface.c b/src/cairo-tee-surface.c index 7a94c9bca..1d075a29c 100644 --- a/src/cairo-tee-surface.c +++ b/src/cairo-tee-surface.c @@ -44,7 +44,6 @@ #include "cairo-default-context-private.h" #include "cairo-error-private.h" -#include "cairo-tee-surface-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-wrapper-private.h" #include "cairo-array-private.h" @@ -220,12 +219,12 @@ _cairo_tee_surface_paint (void *abstract_surface, num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { - status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip); + status = _cairo_surface_wrapper_paint (&slaves[n], op, source, 0, clip); if (unlikely (status)) return status; } - return _cairo_surface_wrapper_paint (&surface->master, op, source, clip); + return _cairo_surface_wrapper_paint (&surface->master, op, source, 0, clip); } static cairo_int_status_t @@ -244,13 +243,17 @@ _cairo_tee_surface_mask (void *abstract_surface, slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_mask (&slaves[n], - op, source, mask, clip); + op, source, 0, + mask, 0, + clip); if (unlikely (status)) return status; } return _cairo_surface_wrapper_mask (&surface->master, - op, source, mask, clip); + op, source, 0, + mask, 0, + clip); } static cairo_int_status_t @@ -274,7 +277,7 @@ _cairo_tee_surface_stroke (void *abstract_surface, slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_stroke (&slaves[n], - op, source, + op, source, 0, path, style, ctm, ctm_inverse, tolerance, antialias, @@ -284,7 +287,7 @@ _cairo_tee_surface_stroke (void *abstract_surface, } return _cairo_surface_wrapper_stroke (&surface->master, - op, source, + op, source, 0, path, style, ctm, ctm_inverse, tolerance, antialias, @@ -310,7 +313,7 @@ _cairo_tee_surface_fill (void *abstract_surface, slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_fill (&slaves[n], - op, source, + op, source, 0, path, fill_rule, tolerance, antialias, clip); @@ -319,7 +322,7 @@ _cairo_tee_surface_fill (void *abstract_surface, } return _cairo_surface_wrapper_fill (&surface->master, - op, source, + op, source, 0, path, fill_rule, tolerance, antialias, clip); @@ -361,7 +364,7 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface, for (n = 0; n < num_slaves; n++) { memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, - source, + source, 0, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, @@ -374,7 +377,7 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface, memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, - source, + source, 0, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, @@ -561,43 +564,3 @@ cairo_tee_surface_index (cairo_surface_t *abstract_surface, return slave->target; } } - -cairo_surface_t * -_cairo_tee_surface_find_match (void *abstract_surface, - const cairo_surface_backend_t *backend, - cairo_content_t content) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int num_slaves, n; - - /* exact match first */ - if (surface->master.target->backend == backend && - surface->master.target->content == content) - { - return surface->master.target; - } - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (slaves[n].target->backend == backend && - slaves[n].target->content == content) - { - return slaves[n].target; - } - } - - /* matching backend? */ - if (surface->master.target->backend == backend) - return surface->master.target; - - num_slaves = _cairo_array_num_elements (&surface->slaves); - slaves = _cairo_array_index (&surface->slaves, 0); - for (n = 0; n < num_slaves; n++) { - if (slaves[n].target->backend == backend) - return slaves[n].target; - } - - return NULL; -} diff --git a/src/cairo-time.c b/src/cairo-time.c index c93205908..05b64e157 100644 --- a/src/cairo-time.c +++ b/src/cairo-time.c @@ -62,7 +62,6 @@ _cairo_time_get (void) } #elif _WIN32 -#define WIN32_LEAN_AND_MEAN #include <windows.h> static cairo_always_inline double diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c index ef7122609..c27eebefc 100644 --- a/src/cairo-toy-font-face.c +++ b/src/cairo-toy-font-face.c @@ -235,7 +235,7 @@ _cairo_toy_font_face_keys_equal (const void *key_a, * @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" + * These font faces are used in implementation of the #cairo_t "toy" * font API. * * If @family is the zero-length string "", the platform-specific default diff --git a/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c index 3414fc268..d1402d2a3 100644 --- a/src/cairo-traps-compositor.c +++ b/src/cairo-traps-compositor.c @@ -1240,7 +1240,7 @@ composite_aligned_boxes (const cairo_traps_compositor_t *compositor, recording_clip = _cairo_clip_from_boxes (boxes); status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source), - m, dst, recording_clip); + m, dst, recording_clip, FALSE); _cairo_clip_destroy (recording_clip); return status; diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c index c58b96665..78c7dd5ec 100644 --- a/src/cairo-truetype-subset.c +++ b/src/cairo-truetype-subset.c @@ -1,3 +1,4 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc @@ -1531,13 +1532,7 @@ find_name (tt_name_t *name, unsigned long size, int name_id, int platform, int e } } if (has_tag) { - p = _cairo_malloc (len - 6); - if (unlikely (p == NULL)) { - status =_cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - memcpy (p, str + 7, len - 7); - p[len-7] = 0; + p = _cairo_strndup (str + 7, len - 7); free (str); str = p; } diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c index 05ef417dc..53c029493 100644 --- a/src/cairo-type3-glyph-surface.c +++ b/src/cairo-type3-glyph-surface.c @@ -182,7 +182,9 @@ _cairo_type3_glyph_surface_finish (void *abstract_surface) { cairo_type3_glyph_surface_t *surface = abstract_surface; - return _cairo_pdf_operators_fini (&surface->pdf_operators); + cairo_status_t status = _cairo_pdf_operators_fini (&surface->pdf_operators); + _cairo_surface_clipper_reset (&surface->clipper); + return status; } static cairo_int_status_t @@ -205,6 +207,9 @@ _cairo_type3_glyph_surface_paint (void *abstract_surface, return status; pattern = (const cairo_surface_pattern_t *) source; + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (unlikely (status)) @@ -289,33 +294,7 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { - cairo_type3_glyph_surface_t *surface = abstract_surface; - cairo_int_status_t status; - cairo_scaled_font_t *font; - cairo_matrix_t new_ctm; - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (status)) - return status; - - cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &scaled_font->ctm); - font = cairo_scaled_font_create (scaled_font->font_face, - &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, - glyphs, num_glyphs, - NULL, 0, - FALSE, - font); - - cairo_scaled_font_destroy (font); - - return status; + return CAIRO_INT_STATUS_IMAGE_FALLBACK; } static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { @@ -478,12 +457,22 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, _cairo_type3_glyph_surface_set_stream (surface, stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); + + /* Check if this is a color glyph and bail out if it is */ status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, NULL, /* foreground color */ &scaled_glyph); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + NULL, /* foreground color */ + &scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index a9980f3b9..736a1bcb3 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -187,6 +187,11 @@ typedef enum _cairo_round_glyph_positions { CAIRO_ROUND_GLYPH_POS_OFF } cairo_round_glyph_positions_t; +typedef struct { + unsigned int index; + double red, green, blue, alpha; +} cairo_palette_color_t; + struct _cairo_font_options { cairo_antialias_t antialias; cairo_subpixel_order_t subpixel_order; @@ -197,6 +202,8 @@ struct _cairo_font_options { char *variations; cairo_color_mode_t color_mode; unsigned int palette_index; + cairo_palette_color_t *custom_palette; + unsigned int custom_palette_size; }; struct _cairo_glyph_text_info { diff --git a/src/cairo-uninstalled.pc.in b/src/cairo-uninstalled.pc.in deleted file mode 100644 index 9dc3231ae..000000000 --- a/src/cairo-uninstalled.pc.in +++ /dev/null @@ -1,8 +0,0 @@ -Name: cairo -Description: Multi-platform 2D graphics library -Version: @VERSION@ - -@PKGCONFIG_REQUIRES@: @CAIRO_REQUIRES@ -Libs: ${pc_top_builddir}/${pcfiledir}/src/libcairo.la -Libs.private: @CAIRO_NONPKGCONFIG_LIBS@ -Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@/src diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index fd989eaa0..ee11d864c 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -95,13 +95,19 @@ typedef struct _cairo_user_scaled_font { double snap_x_scale; double snap_y_scale; + cairo_pattern_t *foreground_marker; + cairo_pattern_t *foreground_pattern; + cairo_bool_t foreground_marker_used; + cairo_bool_t foreground_colors_used; + } cairo_user_scaled_font_t; /* #cairo_user_scaled_font_t */ static cairo_surface_t * -_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font, - cairo_bool_t color) +_cairo_user_scaled_font_create_recording_surface (cairo_user_scaled_font_t *scaled_font, + cairo_bool_t color, + const cairo_color_t *foreground_color) { cairo_content_t content; @@ -113,10 +119,20 @@ _cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t CAIRO_CONTENT_ALPHA; } + if (scaled_font->foreground_pattern) + cairo_pattern_destroy (scaled_font->foreground_pattern); + + scaled_font->foreground_marker_used = FALSE; + scaled_font->foreground_colors_used = FALSE; + if (foreground_color) { + scaled_font->foreground_pattern = _cairo_pattern_create_solid (foreground_color); + } else { + scaled_font->foreground_pattern = cairo_pattern_create_rgb (0, 0, 0); + } + return cairo_recording_surface_create (content, NULL); } - static cairo_t * _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font, cairo_surface_t *recording_surface, @@ -143,7 +159,8 @@ _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t static cairo_int_status_t _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) + cairo_scaled_glyph_t *scaled_glyph, + const cairo_color_t *foreground_color) { cairo_user_font_face_t *face = (cairo_user_font_face_t *) scaled_font->base.font_face; @@ -151,29 +168,27 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon cairo_surface_t *recording_surface = NULL; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_t *cr; + cairo_bool_t foreground_used = FALSE; if (!face->scaled_font_methods.render_color_glyph && !face->scaled_font_methods.render_glyph) return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */ if (_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { - recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE); + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color); _cairo_scaled_glyph_set_recording_surface (scaled_glyph, &scaled_font->base, - recording_surface); + recording_surface, + NULL); } else { status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - if (face->scaled_font_methods.render_color_glyph) { - cairo_pattern_t *pattern; - - recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE); + if (face->scaled_font_methods.render_color_glyph && + scaled_font->base.options.color_mode != CAIRO_COLOR_MODE_NO_COLOR) + { + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, TRUE, foreground_color); cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, TRUE); - pattern = cairo_pattern_create_rgb (0, 0, 0); - pattern->is_userfont_foreground = TRUE; - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); status = face->scaled_font_methods.render_color_glyph ((cairo_scaled_font_t *)scaled_font, _cairo_scaled_glyph_index(scaled_glyph), cr, &extents); @@ -182,14 +197,16 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon scaled_glyph->color_glyph = TRUE; scaled_glyph->color_glyph_set = TRUE; } + cairo_destroy (cr); + foreground_used = scaled_font->foreground_marker_used || scaled_font->foreground_colors_used; } if (status == (cairo_int_status_t)CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED && face->scaled_font_methods.render_glyph) { if (recording_surface) cairo_surface_destroy (recording_surface); - recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE); + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color); recording_surface->device_transform.x0 = .25 * _cairo_scaled_glyph_xphase (scaled_glyph); recording_surface->device_transform.y0 = .25 * _cairo_scaled_glyph_yphase (scaled_glyph); @@ -205,6 +222,7 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon } cairo_destroy (cr); + foreground_used = FALSE; } if (status != CAIRO_INT_STATUS_SUCCESS) { @@ -215,7 +233,8 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon _cairo_scaled_glyph_set_recording_surface (scaled_glyph, &scaled_font->base, - recording_surface); + recording_surface, + foreground_used ? foreground_color : NULL); } /* set metrics */ @@ -266,6 +285,7 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t *scaled_font, cairo_format_t format; int width, height; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_bool_t foreground_used; /* TODO * extend the glyph cache to support argb glyphs. @@ -310,21 +330,25 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t *scaled_font, if (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) { status = _cairo_recording_surface_replay_with_foreground_color (scaled_glyph->recording_surface, surface, - foreground_color); + foreground_color, + &foreground_used); + } else { status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, surface); + foreground_used = FALSE; } - if (unlikely (status)) { cairo_surface_destroy(surface); return status; } + foreground_used = foreground_used || scaled_glyph->recording_uses_foreground_color; + if (info == CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) { _cairo_scaled_glyph_set_color_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t *)surface, - TRUE); + foreground_used ? foreground_color : NULL); surface = NULL; } else { _cairo_scaled_glyph_set_surface (scaled_glyph, @@ -339,6 +363,18 @@ _cairo_user_scaled_glyph_init_surface (cairo_user_scaled_font_t *scaled_font, return status; } +static void +_cairo_user_scaled_glyph_fini (void *abstract_font) +{ + cairo_user_scaled_font_t *scaled_font = abstract_font; + + if (scaled_font->foreground_pattern) + cairo_pattern_destroy (scaled_font->foreground_pattern); + + if (scaled_font->foreground_marker) + cairo_pattern_destroy (scaled_font->foreground_marker); +} + static cairo_int_status_t _cairo_user_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, @@ -348,8 +384,8 @@ _cairo_user_scaled_glyph_init (void *abstract_font, cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_user_scaled_font_t *scaled_font = abstract_font; - if (!scaled_glyph->recording_surface) { - status = _cairo_user_scaled_glyph_init_record_glyph (scaled_font, scaled_glyph); + if (!scaled_glyph->recording_surface || (info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE)) { + status = _cairo_user_scaled_glyph_init_record_glyph (scaled_font, scaled_glyph, foreground_color); if (status) return status; } @@ -509,7 +545,7 @@ _cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { CAIRO_FONT_TYPE_USER, - NULL, /* scaled_font_fini */ + _cairo_user_scaled_glyph_fini, _cairo_user_scaled_glyph_init, _cairo_user_text_to_glyphs, _cairo_user_ucs4_to_index, @@ -551,6 +587,9 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ return status; } + user_scaled_font->foreground_pattern = NULL; + user_scaled_font->foreground_marker = _cairo_pattern_create_foreground_marker (); + /* XXX metrics hinting? */ /* compute a normalized version of font scale matrix to compute @@ -559,6 +598,8 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ { double fixed_scale, x_scale, y_scale; + user_scaled_font->snap_x_scale = 1.0; + user_scaled_font->snap_y_scale = 1.0; user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse; status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale, &x_scale, &y_scale, @@ -597,7 +638,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ cairo_surface_t *recording_surface; cairo_t *cr; - recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE); + recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font, FALSE, NULL); cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface, FALSE); cairo_surface_destroy (recording_surface); @@ -1052,3 +1093,119 @@ cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face) user_font_face = (cairo_user_font_face_t *) font_face; return user_font_face->scaled_font_methods.unicode_to_glyph; } + +/** + * cairo_user_scaled_font_get_foreground_marker: + * @scaled_font: A user scaled font + * + * Gets the foreground pattern of the glyph currently being + * rendered. A #cairo_user_scaled_font_render_glyph_func_t function + * that has been set with + * cairo_user_font_face_set_render_color_glyph_func() may call this + * function to retrieve the current foreground pattern for the glyph + * being rendered. The function should not be called outside of a + * cairo_user_font_face_set_render_color_glyph_func() callback. + * + * The foreground marker pattern contains an internal marker to + * indicate that it is to be substituted with the current source when + * rendered to a surface. Querying the foreground marker will reveal a + * solid black color, however this is not representative of the color + * that will actually be used. Similarly, setting a solid black color + * will render black, not the foreground pattern when the glyph is + * painted to a surface. Using the foreground marker as the source + * instead of cairo_user_scaled_font_get_foreground_source() in a + * color render callback has the following benefits: + * + * 1. Cairo only needs to call the render callback once as it can + * cache the recording. Cairo will substitute the actual foreground + * color when rendering the recording. + * + * 2. On backends that have the concept of a foreground color in fonts such as + * PDF, PostScript, and SVG, cairo can generate more optimal + * output. The glyph can be included in an embedded font. + * + * The one drawback of the using foreground marker is the render + * callback can not access the color components of the pattern as the + * actual foreground pattern is not available at the time the render + * callback is invoked. If the render callback needs to query the + * foreground pattern, use + * cairo_user_scaled_font_get_foreground_source(). + * + * If the render callback simply wants to call cairo_set_source() with + * the foreground pattern, + * cairo_user_scaled_font_get_foreground_marker() is the preferred + * function to use as it results in better performance than + * cairo_user_scaled_font_get_foreground_source(). + * + * Return value: the current foreground source marker pattern. This + * object is owned by cairo. This object must not be modified or used + * outside of a color render callback. To keep a reference to it, + * you must call cairo_pattern_reference(). + * + * Since: 1.18 + **/ +cairo_pattern_t * +cairo_user_scaled_font_get_foreground_marker (cairo_scaled_font_t *scaled_font) +{ + cairo_user_scaled_font_t *user_scaled_font; + + if (scaled_font->backend != &_cairo_user_scaled_font_backend) + return _cairo_pattern_create_in_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); + + user_scaled_font = (cairo_user_scaled_font_t *)scaled_font; + return user_scaled_font->foreground_marker; +} + +/** + * cairo_user_scaled_font_get_foreground_source: + * @scaled_font: A user scaled font + * + * Gets the foreground pattern of the glyph currently being + * rendered. A #cairo_user_scaled_font_render_glyph_func_t function + * that has been set with + * cairo_user_font_face_set_render_color_glyph_func() may call this + * function to retrieve the current foreground pattern for the glyph + * being rendered. The function should not be called outside of a + * cairo_user_font_face_set_render_color_glyph_func() callback. + * + * This function returns the current source at the time the glyph is + * rendered. Compared with + * cairo_user_scaled_font_get_foreground_marker(), this function + * returns the actual source pattern that will be used to render the + * glyph. The render callback is free to query the pattern and + * extract color components or other pattern data. For example if the + * render callback wants to create a gradient stop based on colors in + * the foreground source pattern, it will need to use this function in + * order to be able to query the colors in the foreground pattern. + * + * While this function does not have the restrictions on using the + * pattern that cairo_user_scaled_font_get_foreground_marker() has, it + * does incur a performance penalty. If a render callback calls this + * function: + * + * 1. Cairo will call the render callback whenever the current pattern + * of the context in which the glyph is rendered changes. + * + * 2. On backends that support font embedding (PDF, PostScript, and + * SVG), cairo can not embed this glyph in a font. Instead the glyph + * will be emitted as an image or sequence of drawing operations each + * time it is used. + * + * Return value: the current foreground source pattern. This object is + * owned by cairo. To keep a reference to it, you must call + * cairo_pattern_reference(). + * + * Since: 1.18 + **/ +cairo_pattern_t * +cairo_user_scaled_font_get_foreground_source (cairo_scaled_font_t *scaled_font) +{ + cairo_user_scaled_font_t *user_scaled_font; + + if (scaled_font->backend != &_cairo_user_scaled_font_backend) + return _cairo_pattern_create_in_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); + + user_scaled_font = (cairo_user_scaled_font_t *)scaled_font; + user_scaled_font->foreground_colors_used = TRUE; + return user_scaled_font->foreground_pattern; +} diff --git a/src/cairo-version.h b/src/cairo-version.h index b64b48902..3ac065f68 100644 --- a/src/cairo-version.h +++ b/src/cairo-version.h @@ -3,6 +3,6 @@ #define CAIRO_VERSION_MAJOR 1 #define CAIRO_VERSION_MINOR 17 -#define CAIRO_VERSION_MICRO 7 +#define CAIRO_VERSION_MICRO 9 #endif diff --git a/src/cairo-wgl-context.c b/src/cairo-wgl-context.c deleted file mode 100644 index 487237446..000000000 --- a/src/cairo-wgl-context.c +++ /dev/null @@ -1,261 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2009 Eric Anholt - * Copyright © 2009 Chris Wilson - * Copyright © 2005 Red Hat, Inc - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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> - * Zoxc <zoxc32@gmail.com> - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include "cairo-error-private.h" - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -typedef struct _cairo_wgl_context { - cairo_gl_context_t base; - - HDC dummy_dc; - HWND dummy_wnd; - HGLRC rc; - - HDC prev_dc; - HGLRC prev_rc; -} cairo_wgl_context_t; - -typedef struct _cairo_wgl_surface { - cairo_gl_surface_t base; - - HDC dc; -} cairo_wgl_surface_t; - -static void -_wgl_acquire (void *abstract_ctx) -{ - cairo_wgl_context_t *ctx = abstract_ctx; - - HDC current_dc; - - ctx->prev_dc = wglGetCurrentDC (); - ctx->prev_rc = wglGetCurrentContext (); - - if (ctx->base.current_target == NULL || - _cairo_gl_surface_is_texture (ctx->base.current_target)) - { - current_dc = ctx->dummy_dc; - } - else - { - cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) ctx->base.current_target; - current_dc = surface->dc; - } - - if (ctx->prev_dc != current_dc || - (ctx->prev_rc != ctx->rc && - current_dc != ctx->dummy_dc)) - { - wglMakeCurrent (current_dc, ctx->rc); - } -} - -static void -_wgl_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) -{ - cairo_wgl_context_t *ctx = abstract_ctx; - cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface; - - /* Set the window as the target of our context. */ - wglMakeCurrent (surface->dc, ctx->rc); -} - -static void -_wgl_release (void *abstract_ctx) -{ - cairo_wgl_context_t *ctx = abstract_ctx; - - if (ctx->prev_dc != wglGetCurrentDC () || - ctx->prev_rc != wglGetCurrentContext ()) - { - wglMakeCurrent (ctx->prev_dc, - ctx->prev_rc); - } -} - -static void -_wgl_swap_buffers (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface; - - SwapBuffers (surface->dc); -} - -static void -_wgl_destroy (void *abstract_ctx) -{ - cairo_wgl_context_t *ctx = abstract_ctx; - - if (ctx->dummy_dc != 0) { - wglMakeCurrent (ctx->dummy_dc, 0); - ReleaseDC (ctx->dummy_wnd, ctx->dummy_dc); - DestroyWindow (ctx->dummy_wnd); - } -} - -static cairo_status_t -_wgl_dummy_ctx (cairo_wgl_context_t *ctx) -{ - WNDCLASSEXA wincl; - PIXELFORMATDESCRIPTOR pfd; - int format; - HDC dc; - - ZeroMemory (&wincl, sizeof (WNDCLASSEXA)); - wincl.cbSize = sizeof (WNDCLASSEXA); - wincl.hInstance = GetModuleHandle (0); - wincl.lpszClassName = "cairo_wgl_context_dummy"; - wincl.lpfnWndProc = DefWindowProcA; - wincl.style = CS_OWNDC; - - RegisterClassExA (&wincl); - - ctx->dummy_wnd = CreateWindowA ("cairo_wgl_context_dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - ctx->dummy_dc = GetDC (ctx->dummy_wnd); - - ZeroMemory (&pfd, sizeof (PIXELFORMATDESCRIPTOR)); - pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 24; - pfd.cDepthBits = 16; - pfd.iLayerType = PFD_MAIN_PLANE; - - format = ChoosePixelFormat (ctx->dummy_dc, &pfd); - SetPixelFormat (ctx->dummy_dc, format, &pfd); - - wglMakeCurrent(ctx->dummy_dc, ctx->rc); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_device_t * -cairo_wgl_device_create (HGLRC rc) -{ - cairo_wgl_context_t *ctx; - cairo_status_t status; - - ctx = calloc (1, sizeof (cairo_wgl_context_t)); - if (unlikely (ctx == NULL)) - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - - ctx->rc = rc; - ctx->prev_dc = 0; - ctx->prev_rc = 0; - - status = _wgl_dummy_ctx (ctx); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - ctx->base.acquire = _wgl_acquire; - ctx->base.release = _wgl_release; - ctx->base.make_current = _wgl_make_current; - ctx->base.swap_buffers = _wgl_swap_buffers; - ctx->base.destroy = _wgl_destroy; - - status = _cairo_gl_dispatch_init (&ctx->base.dispatch, - (cairo_gl_get_proc_addr_func_t) wglGetProcAddress); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - status = _cairo_gl_context_init (&ctx->base); - if (unlikely (status)) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - ctx->base.release (ctx); - - return &ctx->base.base; -} - -HGLRC -cairo_wgl_device_get_context (cairo_device_t *device) -{ - cairo_wgl_context_t *ctx; - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { - _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - return NULL; - } - - ctx = (cairo_wgl_context_t *) device; - - return ctx->rc; -} - -cairo_surface_t * -cairo_gl_surface_create_for_dc (cairo_device_t *device, - HDC dc, - int width, - int height) -{ - cairo_wgl_surface_t *surface; - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - if (device->backend->type != CAIRO_DEVICE_TYPE_GL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); - - if (width <= 0 || height <= 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - surface = calloc (1, sizeof (cairo_wgl_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (device, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - surface->dc = dc; - - return &surface->base.base; -} diff --git a/src/cairo-win32.h b/src/cairo-win32.h index 078a70c7b..db4cac69f 100644 --- a/src/cairo-win32.h +++ b/src/cairo-win32.h @@ -107,17 +107,6 @@ cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, #endif /* CAIRO_HAS_WIN32_FONT */ -#if CAIRO_HAS_DWRITE_FONT - -/* - * Win32 DirectWrite font support - */ - -cairo_public cairo_font_face_t * -cairo_dwrite_font_face_create_for_dwrite_fontface (void *dwrite_font_face); - -#endif /* CAIRO_HAS_DWRITE_FONT */ - CAIRO_END_DECLS #else /* CAIRO_HAS_WIN32_SURFACE */ diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c index 5993aa378..a4441dc46 100644 --- a/src/cairo-xcb-surface-render.c +++ b/src/cairo-xcb-surface-render.c @@ -1112,7 +1112,8 @@ record_to_picture (cairo_surface_t *target, status = _cairo_recording_surface_replay_with_clip (source, &matrix, tmp, - NULL); + NULL, + FALSE); if (unlikely (status)) { cairo_surface_destroy (tmp); return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); @@ -1242,12 +1243,6 @@ _cairo_xcb_surface_picture (cairo_xcb_surface_t *target, } } #endif -#if CAIRO_HAS_GL_FUNCTIONS - else if (source->type == CAIRO_SURFACE_TYPE_GL) - { - /* pixmap from texture */ - } -#endif else if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { /* We have to skip the call to attach_snapshot() because we possibly @@ -1266,7 +1261,8 @@ _cairo_xcb_surface_picture (cairo_xcb_surface_t *target, if (unlikely (status)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); - if (image->format != CAIRO_FORMAT_INVALID) { + if (image->format != CAIRO_FORMAT_INVALID && + image->format < ARRAY_LENGTH (target->screen->connection->standard_formats)) { xcb_render_pictformat_t format; format = target->screen->connection->standard_formats[image->format]; diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c index 4c3b99d9e..63e155e00 100644 --- a/src/cairo-xlib-source.c +++ b/src/cairo-xlib-source.c @@ -920,7 +920,8 @@ record_source (cairo_xlib_surface_t *dst, recording = recording_pattern_get_surface (&pattern->base), status = _cairo_recording_surface_replay_with_clip (recording, &matrix, &src->base, - NULL); + NULL, + FALSE); cairo_surface_destroy (recording); if (unlikely (status)) { cairo_surface_destroy (&src->base); diff --git a/src/cairo-xml-surface.c b/src/cairo-xml-surface.c deleted file mode 100644 index 401a5b3c6..000000000 --- a/src/cairo-xml-surface.c +++ /dev/null @@ -1,1212 +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 © 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 surface is intended to produce a verbose, hierarchical, DAG XML file - * representing a single surface. It is intended to be used by debuggers, - * such as cairo-sphinx, or by application test-suites that want a log of - * operations. - */ - -#include "cairoint.h" - -#include "cairo-xml.h" - -#include "cairo-clip-private.h" -#include "cairo-device-private.h" -#include "cairo-default-context-private.h" -#include "cairo-image-surface-private.h" -#include "cairo-error-private.h" -#include "cairo-output-stream-private.h" -#include "cairo-recording-surface-inline.h" - -#define static cairo_warn static - -typedef struct _cairo_xml_surface cairo_xml_surface_t; - -typedef struct _cairo_xml { - cairo_device_t base; - - cairo_output_stream_t *stream; - int indent; -} cairo_xml_t; - -struct _cairo_xml_surface { - cairo_surface_t base; - - double width, height; -}; - -slim_hidden_proto (cairo_xml_for_recording_surface); - -static const cairo_surface_backend_t _cairo_xml_surface_backend; - -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 */ - - "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ - "SCREEN", /* CAIRO_OPERATOR_SCREEN */ - "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ - "DARKEN", /* CAIRO_OPERATOR_DARKEN */ - "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ - "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ - "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ - "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ - "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ - "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ - "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ - "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ - "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ - "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ - "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ - }; - 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[] = { - "DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ - "NONE", /* CAIRO_ANTIALIAS_NONE */ - "GRAY", /* CAIRO_ANTIALIAS_GRAY */ - "SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */ - "FAST", /* CAIRO_ANTIALIAS_FAST */ - "GOOD", /* CAIRO_ANTIALIAS_GOOD */ - "BEST", /* CAIRO_ANTIALIAS_BEST */ - }; - 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 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 const char * -_format_to_string (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_ARGB32: return "ARGB32"; - case CAIRO_FORMAT_RGB30: return "RGB30"; - case CAIRO_FORMAT_RGB24: return "RGB24"; - case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; - case CAIRO_FORMAT_RGB96F: return "RGB96F"; - case CAIRO_FORMAT_RGBA128F: return "RGBA128F"; - case CAIRO_FORMAT_A8: return "A8"; - case CAIRO_FORMAT_A1: return "A1"; - case CAIRO_FORMAT_INVALID: return "INVALID"; - } - ASSERT_NOT_REACHED; - return "INVALID"; -} - -static cairo_status_t -_device_flush (void *abstract_device) -{ - cairo_xml_t *xml = abstract_device; - cairo_status_t status; - - status = _cairo_output_stream_flush (xml->stream); - - return status; -} - -static void -_device_destroy (void *abstract_device) -{ - cairo_xml_t *xml = abstract_device; - cairo_status_t status; - - status = _cairo_output_stream_destroy (xml->stream); - - free (xml); -} - -static const cairo_device_backend_t _cairo_xml_device_backend = { - CAIRO_DEVICE_TYPE_XML, - - NULL, NULL, /* lock, unlock */ - - _device_flush, - NULL, /* finish */ - _device_destroy -}; - -static cairo_device_t * -_cairo_xml_create_internal (cairo_output_stream_t *stream) -{ - cairo_xml_t *xml; - - xml = _cairo_malloc (sizeof (cairo_xml_t)); - if (unlikely (xml == NULL)) - return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); - - memset (xml, 0, sizeof (cairo_xml_t)); - - _cairo_device_init (&xml->base, &_cairo_xml_device_backend); - - xml->indent = 0; - xml->stream = stream; - - return &xml->base; -} - -static void -_cairo_xml_indent (cairo_xml_t *xml, int indent) -{ - xml->indent += indent; - assert (xml->indent >= 0); -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) -{ - va_list ap; - char indent[80]; - int len; - - len = MIN (xml->indent, ARRAY_LENGTH (indent)); - memset (indent, ' ', len); - _cairo_output_stream_write (xml->stream, indent, len); - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); - - _cairo_output_stream_write (xml->stream, "\n", 1); -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) -{ - char indent[80]; - int len; - - len = MIN (xml->indent, ARRAY_LENGTH (indent)); - memset (indent, ' ', len); - _cairo_output_stream_write (xml->stream, indent, len); - - if (fmt != NULL) { - va_list ap; - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); - } -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); -} - -static void CAIRO_PRINTF_FORMAT (2, 3) -_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) -{ - if (fmt != NULL) { - va_list ap; - - va_start (ap, fmt); - _cairo_output_stream_vprintf (xml->stream, fmt, ap); - va_end (ap); - } - - _cairo_output_stream_write (xml->stream, "\n", 1); -} - -static cairo_surface_t * -_cairo_xml_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - cairo_rectangle_t extents; - - extents.x = extents.y = 0; - extents.width = width; - extents.height = height; - - return cairo_recording_surface_create (content, &extents); -} - -static cairo_bool_t -_cairo_xml_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_xml_surface_t *surface = abstract_surface; - - if (surface->width < 0 || surface->height < 0) - return FALSE; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static cairo_status_t -_cairo_xml_move_to (void *closure, - const cairo_point_t *p1) -{ - _cairo_xml_printf_continue (closure, " %f %f m", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_line_to (void *closure, - const cairo_point_t *p1) -{ - _cairo_xml_printf_continue (closure, " %f %f l", - _cairo_fixed_to_double (p1->x), - _cairo_fixed_to_double (p1->y)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_curve_to (void *closure, - const cairo_point_t *p1, - const cairo_point_t *p2, - const cairo_point_t *p3) -{ - _cairo_xml_printf_continue (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 -_cairo_xml_close_path (void *closure) -{ - _cairo_xml_printf_continue (closure, " h"); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xml_emit_path (cairo_xml_t *xml, - const cairo_path_fixed_t *path) -{ - cairo_status_t status; - - _cairo_xml_printf_start (xml, "<path>"); - status = _cairo_path_fixed_interpret (path, - _cairo_xml_move_to, - _cairo_xml_line_to, - _cairo_xml_curve_to, - _cairo_xml_close_path, - xml); - assert (status == CAIRO_STATUS_SUCCESS); - _cairo_xml_printf_end (xml, "</path>"); -} - -static void -_cairo_xml_emit_string (cairo_xml_t *xml, - const char *node, - const char *data) -{ - _cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node); -} - -static void -_cairo_xml_emit_double (cairo_xml_t *xml, - const char *node, - double data) -{ - _cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node); -} - -static cairo_xml_t * -to_xml (cairo_xml_surface_t *surface) -{ - return (cairo_xml_t *) surface->base.device; -} - -static cairo_status_t -_cairo_xml_surface_emit_clip_boxes (cairo_xml_surface_t *surface, - const cairo_clip_t *clip) -{ - cairo_box_t *box; - cairo_xml_t *xml; - int n; - - if (clip->num_boxes == 0) - return CAIRO_STATUS_SUCCESS; - - /* skip the trivial clip covering the surface extents */ - if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) { - box = &clip->boxes[0]; - if (box->p1.x <= 0 && box->p1.y <= 0 && - box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) && - box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height)) - { - return CAIRO_STATUS_SUCCESS; - } - } - - xml = to_xml (surface); - - _cairo_xml_printf (xml, "<clip>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_printf (xml, "<path>"); - _cairo_xml_indent (xml, 2); - for (n = 0; n < clip->num_boxes; n++) { - box = &clip->boxes[n]; - - _cairo_xml_printf_start (xml, "%f %f m", - _cairo_fixed_to_double (box->p1.x), - _cairo_fixed_to_double (box->p1.y)); - _cairo_xml_printf_continue (xml, " %f %f l", - _cairo_fixed_to_double (box->p2.x), - _cairo_fixed_to_double (box->p1.y)); - _cairo_xml_printf_continue (xml, " %f %f l", - _cairo_fixed_to_double (box->p2.x), - _cairo_fixed_to_double (box->p2.y)); - _cairo_xml_printf_continue (xml, " %f %f l", - _cairo_fixed_to_double (box->p1.x), - _cairo_fixed_to_double (box->p2.y)); - _cairo_xml_printf_end (xml, " h"); - } - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</path>"); - _cairo_xml_emit_double (xml, "tolerance", 1.0); - _cairo_xml_emit_string (xml, "antialias", - _antialias_to_string (CAIRO_ANTIALIAS_NONE)); - _cairo_xml_emit_string (xml, "fill-rule", - _fill_rule_to_string (CAIRO_FILL_RULE_WINDING)); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</clip>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface, - const cairo_clip_path_t *clip_path) -{ - cairo_box_t box; - cairo_status_t status; - cairo_xml_t *xml; - - if (clip_path == NULL) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev); - if (unlikely (status)) - return status; - - /* skip the trivial clip covering the surface extents */ - if (surface->width >= 0 && surface->height >= 0 && - _cairo_path_fixed_is_box (&clip_path->path, &box)) - { - if (box.p1.x <= 0 && box.p1.y <= 0 && - box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && - box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) - { - return CAIRO_STATUS_SUCCESS; - } - } - - xml = to_xml (surface); - - _cairo_xml_printf_start (xml, "<clip>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_path (xml, &clip_path->path); - _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance); - _cairo_xml_emit_string (xml, "antialias", - _antialias_to_string (clip_path->antialias)); - _cairo_xml_emit_string (xml, "fill-rule", - _fill_rule_to_string (clip_path->fill_rule)); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf_end (xml, "</clip>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, - const cairo_clip_t *clip) -{ - cairo_status_t status; - - if (clip == NULL) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_xml_surface_emit_clip_boxes (surface, clip); - if (unlikely (status)) - return status; - - return _cairo_xml_surface_emit_clip_path (surface, clip->path); -} - -static cairo_status_t -_cairo_xml_emit_solid (cairo_xml_t *xml, - const cairo_solid_pattern_t *solid) -{ - _cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>", - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xml_emit_matrix (cairo_xml_t *xml, - const cairo_matrix_t *matrix) -{ - if (! _cairo_matrix_is_identity (matrix)) { - _cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>", - matrix->xx, matrix->yx, - matrix->xy, matrix->yy, - matrix->x0, matrix->y0); - } -} - -static void -_cairo_xml_emit_gradient (cairo_xml_t *xml, - const cairo_gradient_pattern_t *gradient) -{ - unsigned int i; - - for (i = 0; i < gradient->n_stops; i++) { - _cairo_xml_printf (xml, - "<color-stop>%f %f %f %f %f</color-stop>", - gradient->stops[i].offset, - gradient->stops[i].color.red, - gradient->stops[i].color.green, - gradient->stops[i].color.blue, - gradient->stops[i].color.alpha); - } -} - -static cairo_status_t -_cairo_xml_emit_linear (cairo_xml_t *xml, - const cairo_linear_pattern_t *linear) -{ - _cairo_xml_printf (xml, - "<linear x1='%f' y1='%f' x2='%f' y2='%f'>", - linear->pd1.x, linear->pd1.y, - linear->pd2.x, linear->pd2.y); - _cairo_xml_indent (xml, 2); - _cairo_xml_emit_gradient (xml, &linear->base); - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</linear>"); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_radial (cairo_xml_t *xml, - const cairo_radial_pattern_t *radial) -{ - _cairo_xml_printf (xml, - "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>", - radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius, - radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius); - _cairo_xml_indent (xml, 2); - _cairo_xml_emit_gradient (xml, &radial->base); - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</radial>"); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_write_func (void *closure, const unsigned char *data, unsigned len) -{ - _cairo_output_stream_write (closure, data, len); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_image (cairo_xml_t *xml, - cairo_image_surface_t *image) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - _cairo_xml_printf_start (xml, - "<image width='%d' height='%d' format='%s'>", - image->width, image->height, - _format_to_string (image->format)); - - stream = _cairo_base64_stream_create (xml->stream); - status = cairo_surface_write_to_png_stream (&image->base, - _write_func, stream); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_output_stream_destroy (stream); - if (unlikely (status)) - return status; - - _cairo_xml_printf_end (xml, "</image>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_surface (cairo_xml_t *xml, - const cairo_surface_pattern_t *pattern) -{ - cairo_surface_t *source = pattern->surface; - cairo_status_t status; - - if (_cairo_surface_is_recording (source)) { - status = cairo_xml_for_recording_surface (&xml->base, source); - } else { - cairo_image_surface_t *image; - void *image_extra; - - status = _cairo_surface_acquire_source_image (source, - &image, &image_extra); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_image (xml, image); - - _cairo_surface_release_source_image (source, image, image_extra); - } - - return status; -} - -static cairo_status_t -_cairo_xml_emit_pattern (cairo_xml_t *xml, - const char *source_or_mask, - const cairo_pattern_t *pattern) -{ - cairo_status_t status; - - _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); - _cairo_xml_indent (xml, 2); - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); - break; - default: - ASSERT_NOT_REACHED; - status = CAIRO_INT_STATUS_UNSUPPORTED; - break; - } - - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { - _cairo_xml_emit_matrix (xml, &pattern->matrix); - _cairo_xml_printf (xml, - "<extend>%s</extend>", - _extend_to_string (pattern->extend)); - _cairo_xml_printf (xml, - "<filter>%s</filter>", - _filter_to_string (pattern->filter)); - } - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</%s-pattern>", source_or_mask); - - return status; -} - -static cairo_int_status_t -_cairo_xml_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, "<paint>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</paint>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xml_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, "<mask>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "mask", mask); - if (unlikely (status)) - return status; - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</mask>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xml_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, "<stroke>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - _cairo_xml_emit_double (xml, "line-width", style->line_width); - _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); - _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); - _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - if (style->num_dashes) { - unsigned int i; - - _cairo_xml_printf_start (xml, "<dash offset='%f'>", - style->dash_offset); - for (i = 0; i < style->num_dashes; i++) - _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); - - _cairo_xml_printf_end (xml, "</dash>"); - } - - _cairo_xml_emit_path (xml, path); - _cairo_xml_emit_double (xml, "tolerance", tolerance); - _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); - - _cairo_xml_emit_matrix (xml, ctm); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</stroke>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xml_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t*path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - - _cairo_xml_printf (xml, "<fill>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - _cairo_xml_emit_path (xml, path); - _cairo_xml_emit_double (xml, "tolerance", tolerance); - _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); - _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</fill>"); - - return CAIRO_STATUS_SUCCESS; -} - -#if CAIRO_HAS_FT_FONT -#include "cairo-ft-private.h" -static cairo_status_t -_cairo_xml_emit_type42_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font) -{ - const cairo_scaled_font_backend_t *backend; - cairo_output_stream_t *base64_stream; - cairo_output_stream_t *zlib_stream; - cairo_status_t status, status2; - unsigned long size; - uint32_t len; - 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 = _cairo_malloc (size); - if (unlikely (buf == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); - if (unlikely (status)) { - free (buf); - return status; - } - - _cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>", - _cairo_ft_scaled_font_get_load_flags (scaled_font)); - - - base64_stream = _cairo_base64_stream_create (xml->stream); - len = size; - _cairo_output_stream_write (base64_stream, &len, sizeof (len)); - - zlib_stream = _cairo_deflate_stream_create (base64_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 (base64_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - _cairo_xml_printf_end (xml, "</font>"); - - return status; -} -#else -static cairo_status_t -_cairo_xml_emit_type42_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} -#endif - -static cairo_status_t -_cairo_xml_emit_type3_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - _cairo_xml_printf_start (xml, "<font type='3'>"); - _cairo_xml_printf_end (xml, "</font>"); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xml_emit_scaled_font (cairo_xml_t *xml, - cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_int_status_t status; - - _cairo_xml_printf (xml, "<scaled-font>"); - _cairo_xml_indent (xml, 2); - - status = _cairo_xml_emit_type42_font (xml, scaled_font); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_xml_emit_type3_font (xml, scaled_font, - glyphs, num_glyphs); - } - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</scaled-font>"); - - return status; -} - -static cairo_int_status_t -_cairo_xml_surface_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip) -{ - cairo_xml_surface_t *surface = abstract_surface; - cairo_xml_t *xml = to_xml (surface); - cairo_status_t status; - int i; - - _cairo_xml_printf (xml, "<glyphs>"); - _cairo_xml_indent (xml, 2); - - _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); - - status = _cairo_xml_surface_emit_clip (surface, clip); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_pattern (xml, "source", source); - if (unlikely (status)) - return status; - - status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); - if (unlikely (status)) - return status; - - for (i = 0; i < num_glyphs; i++) { - _cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>", - glyphs[i].index, - glyphs[i].x, - glyphs[i].y); - } - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</glyphs>"); - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t -_cairo_xml_surface_backend = { - CAIRO_SURFACE_TYPE_XML, - NULL, - - _cairo_default_context_create, - - _cairo_xml_surface_create_similar, - NULL, /* create_similar_image */ - NULL, /* map_to_image */ - NULL, /* unmap_image */ - - _cairo_surface_default_source, - NULL, /* acquire source image */ - NULL, /* release source image */ - NULL, /* snapshot */ - - NULL, /* copy page */ - NULL, /* show page */ - - _cairo_xml_surface_get_extents, - NULL, /* get_font_options */ - - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - - _cairo_xml_surface_paint, - _cairo_xml_surface_mask, - _cairo_xml_surface_stroke, - _cairo_xml_surface_fill, - NULL, /* fill_stroke */ - _cairo_xml_surface_glyphs, -}; - -static cairo_surface_t * -_cairo_xml_surface_create_internal (cairo_device_t *device, - cairo_content_t content, - double width, - double height) -{ - cairo_xml_surface_t *surface; - - surface = _cairo_malloc (sizeof (cairo_xml_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &_cairo_xml_surface_backend, - device, - content, - TRUE); /* is_vector */ - - surface->width = width; - surface->height = height; - - return &surface->base; -} - -cairo_device_t * -cairo_xml_create (const char *filename) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_output_stream_create_for_filename (filename); - if ((status = _cairo_output_stream_get_status (stream))) - return _cairo_device_create_in_error (status); - - return _cairo_xml_create_internal (stream); -} - -cairo_device_t * -cairo_xml_create_for_stream (cairo_write_func_t write_func, - void *closure) -{ - cairo_output_stream_t *stream; - cairo_status_t status; - - stream = _cairo_output_stream_create (write_func, NULL, closure); - if ((status = _cairo_output_stream_get_status (stream))) - return _cairo_device_create_in_error (status); - - return _cairo_xml_create_internal (stream); -} - -cairo_surface_t * -cairo_xml_surface_create (cairo_device_t *device, - cairo_content_t content, - double width, double height) -{ - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) - return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); - - return _cairo_xml_surface_create_internal (device, content, width, height); -} - -cairo_status_t -cairo_xml_for_recording_surface (cairo_device_t *device, - cairo_surface_t *recording_surface) -{ - cairo_box_t bbox; - cairo_rectangle_int_t extents; - cairo_surface_t *surface; - cairo_xml_t *xml; - cairo_status_t status; - - if (unlikely (device->status)) - return device->status; - - if (unlikely (recording_surface->status)) - return recording_surface->status; - - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) - return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - - if (unlikely (! _cairo_surface_is_recording (recording_surface))) - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - - status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - surface = _cairo_xml_surface_create_internal (device, - recording_surface->content, - extents.width, - extents.height); - if (unlikely (surface->status)) - return surface->status; - - xml = (cairo_xml_t *) device; - - _cairo_xml_printf (xml, - "<surface content='%s' width='%d' height='%d'>", - _content_to_string (recording_surface->content), - extents.width, extents.height); - _cairo_xml_indent (xml, 2); - - cairo_surface_set_device_offset (surface, -extents.x, -extents.y); - status = _cairo_recording_surface_replay (recording_surface, surface); - cairo_surface_destroy (surface); - - _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, "</surface>"); - - return status; -} -slim_hidden_def (cairo_xml_for_recording_surface); diff --git a/src/cairo.c b/src/cairo.c index f55429405..3d4fea601 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -372,7 +372,8 @@ static const cairo_t _cairo_nil[] = { DEFINE_NIL_CONTEXT (CAIRO_STATUS_FREETYPE_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_WIN32_GDI_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_TAG_ERROR), - DEFINE_NIL_CONTEXT (CAIRO_STATUS_DWRITE_ERROR) + DEFINE_NIL_CONTEXT (CAIRO_STATUS_DWRITE_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_SVG_FONT_ERROR) }; COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1); @@ -706,6 +707,7 @@ 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: @@ -813,6 +815,7 @@ 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: @@ -918,6 +921,8 @@ slim_hidden_def (cairo_set_source_rgb); * range 0 to 1. If the values passed in are outside that range, they * will be clamped. * + * Note that the color and alpha values are not premultiplied. + * * The default source pattern is opaque black, (that is, it is * equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)). * @@ -937,6 +942,7 @@ cairo_set_source_rgba (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_set_source_rgba); /** * cairo_set_source_surface: @@ -1050,6 +1056,7 @@ cairo_get_source (cairo_t *cr) return cr->backend->get_source (cr); } +slim_hidden_def (cairo_get_source); /** * cairo_set_tolerance: @@ -1159,9 +1166,8 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) * cairo_set_line_width() and ignore this note. * * As with the other stroke parameters, the current line width is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. + * examined by cairo_stroke(), and cairo_stroke_extents(), but does not have + * any effect during path construction. * * The default line width value is 2.0. * @@ -1235,9 +1241,8 @@ slim_hidden_def (cairo_set_hairline); * styles are drawn. * * As with the other stroke parameters, the current line cap style is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. + * examined by cairo_stroke(), and cairo_stroke_extents(), but does not have + * any effect during path construction. * * The default line cap style is %CAIRO_LINE_CAP_BUTT. * @@ -1267,9 +1272,8 @@ slim_hidden_def (cairo_set_line_cap); * styles are drawn. * * As with the other stroke parameters, the current line join style is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. + * examined by cairo_stroke(), and cairo_stroke_extents(), but does not have + * any effect during path construction. * * The default line join style is %CAIRO_LINE_JOIN_MITER. * @@ -1338,6 +1342,7 @@ cairo_set_dash (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_set_dash); /** * cairo_get_dash_count: @@ -1403,9 +1408,8 @@ cairo_get_dash (cairo_t *cr, * converted to a bevel. * * As with the other stroke parameters, the current line miter limit is - * examined by cairo_stroke(), cairo_stroke_extents(), and - * cairo_stroke_to_path(), but does not have any effect during path - * construction. + * examined by cairo_stroke(), and cairo_stroke_extents(), but does not have + * any effect during path construction. * * The default miter limit value is 10.0, which will convert joins * with interior angles less than 11 degrees to bevels instead of @@ -1512,6 +1516,7 @@ cairo_rotate (cairo_t *cr, double angle) if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_rotate); /** * cairo_transform: @@ -1587,6 +1592,7 @@ cairo_identity_matrix (cairo_t *cr) if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_identity_matrix); /** * cairo_user_to_device: @@ -1901,6 +1907,7 @@ cairo_arc (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_arc); /** * cairo_arc_negative: @@ -1946,6 +1953,7 @@ cairo_arc_negative (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_arc_negative); /* XXX: NYI void @@ -2128,6 +2136,7 @@ cairo_rectangle (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_rectangle); #if 0 /* XXX: NYI */ @@ -2288,6 +2297,7 @@ cairo_paint_with_alpha (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_paint_with_alpha); /** * cairo_mask: @@ -2463,6 +2473,7 @@ cairo_fill (cairo_t *cr) if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_fill); /** * cairo_fill_preserve: @@ -2754,6 +2765,7 @@ cairo_clip (cairo_t *cr) if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_clip); /** * cairo_clip_preserve: @@ -2860,6 +2872,7 @@ cairo_clip_extents (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_clip_extents); /** * cairo_in_clip: @@ -2924,7 +2937,7 @@ cairo_copy_clip_rectangle_list (cairo_t *cr) * CAIRO_TAG_DEST: * * Create a destination for a hyperlink. Destination tag attributes - * are detailed at [Destinations][dests]. + * are detailed at [Destinations][dest]. * * Since: 1.16 **/ @@ -2933,7 +2946,7 @@ cairo_copy_clip_rectangle_list (cairo_t *cr) * CAIRO_TAG_LINK: * * Create hyperlink. Link tag attributes are detailed at - * [Links][links]. + * [Links][link]. * * Since: 1.16 **/ @@ -3343,7 +3356,7 @@ cairo_set_scaled_font (cairo_t *cr, if (unlikely (cr->status)) return; - if ((scaled_font == NULL)) { + if (scaled_font == NULL) { _cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER)); return; } @@ -4004,6 +4017,7 @@ cairo_has_current_point (cairo_t *cr) return cr->backend->has_current_point (cr); } +slim_hidden_def (cairo_has_current_point); /** * cairo_get_current_point: @@ -4026,7 +4040,7 @@ cairo_has_current_point (cairo_t *cr) * cairo_move_to(), cairo_line_to(), cairo_curve_to(), * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(), * cairo_arc(), cairo_arc_negative(), cairo_rectangle(), - * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path(). + * cairo_text_path(), cairo_glyph_path(). * * Some functions use and alter the current point but do not * otherwise change current path: @@ -4074,6 +4088,7 @@ cairo_get_fill_rule (cairo_t *cr) return cr->backend->get_fill_rule (cr); } +slim_hidden_def (cairo_set_fill_rule); /** * cairo_get_line_width: @@ -4174,6 +4189,7 @@ cairo_get_miter_limit (cairo_t *cr) return cr->backend->get_miter_limit (cr); } +slim_hidden_def (cairo_set_miter_limit); /** * cairo_get_matrix: @@ -4289,6 +4305,7 @@ cairo_copy_path (cairo_t *cr) return cr->backend->copy_path (cr); } +slim_hidden_def (cairo_copy_path); /** * cairo_copy_path_flat: @@ -4383,6 +4400,7 @@ cairo_append_path (cairo_t *cr, if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_append_path); /** * cairo_status: diff --git a/src/cairo.h b/src/cairo.h index 82e2c69d8..eef4c442b 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -157,9 +157,7 @@ typedef struct _cairo_surface cairo_surface_t; * * A #cairo_device_t represents the driver interface for drawing * operations to a #cairo_surface_t. There are different subtypes of - * #cairo_device_t for different drawing backends; for example, - * cairo_egl_device_create() creates a device that wraps an EGL display and - * context. + * #cairo_device_t for different drawing backends. * * The type of a device can be queried with cairo_device_get_type(). * @@ -297,6 +295,7 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_WIN32_GDI_ERROR: error occurred in the Windows Graphics Device Interface (Since 1.16) * @CAIRO_STATUS_TAG_ERROR: invalid tag name, attributes, or nesting (Since 1.16) * @CAIRO_STATUS_DWRITE_ERROR: error occurred in the Windows Direct Write API (Since 1.18) + * @CAIRO_STATUS_SVG_FONT_ERROR: error occurred in OpenType-SVG font rendering (Since 1.18) * @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 @@ -358,6 +357,7 @@ typedef enum _cairo_status { CAIRO_STATUS_WIN32_GDI_ERROR, CAIRO_STATUS_TAG_ERROR, CAIRO_STATUS_DWRITE_ERROR, + CAIRO_STATUS_SVG_FONT_ERROR, CAIRO_STATUS_LAST_STATUS } cairo_status_t; @@ -477,7 +477,7 @@ typedef cairo_status_t (*cairo_read_func_t) (void *closure, /** * cairo_rectangle_int_t: * @x: X coordinate of the left side of the rectangle - * @y: Y coordinate of the the top side of the rectangle + * @y: Y coordinate of the top side of the rectangle * @width: width of the rectangle * @height: height of the rectangle * @@ -1000,7 +1000,7 @@ cairo_clip_extents (cairo_t *cr, /** * cairo_rectangle_t: * @x: X coordinate of the left side of the rectangle - * @y: Y coordinate of the the top side of the rectangle + * @y: Y coordinate of the top side of the rectangle * @width: width of the rectangle * @height: height of the rectangle * @@ -1379,7 +1379,7 @@ typedef enum _cairo_hint_metrics { * contains a color presentation for a glyph, and when supported by * the font backend, the glyph will be rendered in color, since 1.18. * - * Specifies if color fonts are to be rendered using the the color + * Specifies if color fonts are to be rendered using the color * glyphs or outline glyphs. Glyphs that do not have a color * presentation, and non-color fonts are not affected by this font * option. @@ -1485,8 +1485,20 @@ cairo_public void cairo_font_options_set_color_palette (cairo_font_options_t *options, unsigned int palette_index); +cairo_public void +cairo_font_options_set_custom_palette_color (cairo_font_options_t *options, + unsigned int index, + double red, double green, + double blue, double alpha); + +cairo_public cairo_status_t +cairo_font_options_get_custom_palette_color (cairo_font_options_t *options, + unsigned int index, + double *red, double *green, + double *blue, double *alpha); + /* This interface is for dealing with text as text, not caring about the - font object inside the the cairo_t. */ + font object inside the cairo_t. */ cairo_public void cairo_select_font_face (cairo_t *cr, @@ -1802,25 +1814,14 @@ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_ * cairo_user_font_face_set_render_glyph_func(), the result is * undefined if any source other than the default source on @cr is * used. That means, glyph bitmaps should be rendered using - * cairo_mask() instead of cairo_paint(). When this callback is set with - * cairo_user_font_face_set_render_color_glyph_func(), setting the - * source is a valid operation. + * cairo_mask() instead of cairo_paint(). * * When this callback is set with * cairo_user_font_face_set_render_color_glyph_func(), the default - * source is the current source color of the context that is rendering - * the user font. That is, the same color a non-color user font will - * be rendered in. In most cases the callback will want to set a - * specific color. If the callback wishes to use the current context - * color after using another source, it should retain a reference to - * the source or use cairo_save()/cairo_restore() prior to changing - * the source. Note that the default source contains an internal - * marker to indicate that it is to be substituted with the current - * context source color when rendered to a surface. Querying the - * default source pattern will reveal a solid black color, however - * this is not representative of the color that will actually be - * used. Similarly, setting a solid black color will render black, not - * the current context source when the glyph is painted to a surface. + * source is black. Setting the source is a valid + * operation. cairo_user_scaled_font_get_foreground_marker() or + * cairo_user_scaled_font_get_foreground_source() may be called to + * obtain the current source at the time the glyph is rendered. * * Other non-default settings on @cr include a font size of 1.0 (given that * it is set up to be in font space), and font options corresponding to @@ -1843,10 +1844,13 @@ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_ * Where both color and non-color callbacks has been set using * cairo_user_font_face_set_render_color_glyph_func(), and * cairo_user_font_face_set_render_glyph_func(), the color glyph - * callback may return %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if the - * glyph is not a color glyph. This is the only case in which the - * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED may be returned from a - * render callback. + * callback will be called first. If the color glyph callback returns + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, any drawing operations are + * discarded and the non-color callback will be called. This is the + * only case in which the %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED may + * be returned from a render callback. This fallback sequence allows a + * user font face to contain a combination of both color and non-color + * glyphs. * * Returns: %CAIRO_STATUS_SUCCESS upon success, * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, @@ -2014,6 +2018,11 @@ cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face); cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face); +cairo_public cairo_pattern_t * +cairo_user_scaled_font_get_foreground_marker (cairo_scaled_font_t *scaled_font); + +cairo_public cairo_pattern_t * +cairo_user_scaled_font_get_foreground_source (cairo_scaled_font_t *scaled_font); /* Query functions */ @@ -2440,26 +2449,37 @@ cairo_surface_status (cairo_surface_t *surface); * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps, since 1.2 * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib, since 1.2 * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb, since 1.2 - * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz, since 1.2 + * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz, since 1.2, deprecated 1.18 + * (glitz support have been removed, this surface type will never be set by cairo) * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz, since 1.2 * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32, since 1.2 - * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos, since 1.2 - * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb, since 1.2 + * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos, since 1.2, deprecated 1.18 + * (beos support have been removed, this surface type will never be set by cairo) + * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb, since 1.2, deprecated 1.18 + * (directfb support have been removed, this surface type will never be set by cairo) * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg, since 1.2 - * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2, since 1.4 + * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2, since 1.4, deprecated 1.18 + * (os2 support have been removed, this surface type will never be set by cairo) * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface, since 1.6 * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image, since 1.6 * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 - * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10 + * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10, deprecated 1.18 + * (Ot support have been removed, this surface type will never be set by cairo) * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10 - * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10 - * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10 - * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 + * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10, deprecated 1.18 + * (OpenVG support have been removed, this surface type will never be set by cairo) + * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10, deprecated 1.18 + * (OpenGL support have been removed, this surface type will never be set by cairo) + * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10, deprecated 1.18 + * (DRM support have been removed, this surface type will never be set by cairo) * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10 * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10 + * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10, deprecated 1.18 + * (Skia support have been removed, this surface type will never be set by cairo) * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with * cairo_surface_create_for_rectangle(), since 1.10 - * @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl, since 1.12 + * @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl, since 1.12, deprecated 1.18 + * (Cogl support have been removed, this surface type will never be set by cairo) * * #cairo_surface_type_t is used to describe the type of a given * surface. The surface types are also known as "backends" or "surface diff --git a/src/cairo.pc.in b/src/cairo.pc.in deleted file mode 100644 index b361edf18..000000000 --- a/src/cairo.pc.in +++ /dev/null @@ -1,13 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: cairo -Description: Multi-platform 2D graphics library -Version: @VERSION@ - -@PKGCONFIG_REQUIRES@: @CAIRO_REQUIRES@ -Libs: -L${libdir} -lcairo -Libs.private: @CAIRO_NONPKGCONFIG_LIBS@ -Cflags: -I${includedir}/cairo diff --git a/src/cairoint.h b/src/cairoint.h index 987bf9a58..e5c281842 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -73,8 +73,7 @@ #if CAIRO_HAS_PDF_SURFACE || \ CAIRO_HAS_PS_SURFACE || \ - CAIRO_HAS_SCRIPT_SURFACE || \ - CAIRO_HAS_XML_SURFACE + CAIRO_HAS_SCRIPT_SURFACE #define CAIRO_HAS_DEFLATE_STREAM 1 #endif @@ -406,6 +405,10 @@ _cairo_hash_bytes (uintptr_t hash, const void *bytes, unsigned int length); +cairo_private uintptr_t +_cairo_hash_uintptr (uintptr_t hash, + uintptr_t u); + /* We use bits 24-27 to store phases for subpixel positions */ #define _cairo_scaled_glyph_index(g) ((unsigned long)((g)->hash_entry.hash & 0xffffff)) #define _cairo_scaled_glyph_xphase(g) (int)(((g)->hash_entry.hash >> 24) & 3) @@ -893,6 +896,10 @@ cairo_private void _cairo_font_options_init_copy (cairo_font_options_t *options, const cairo_font_options_t *other); +cairo_private cairo_bool_t +_cairo_font_options_compare (const cairo_font_options_t *a, + const cairo_font_options_t *b); + cairo_private void _cairo_font_options_fini (cairo_font_options_t *options); @@ -943,6 +950,13 @@ _cairo_get_locale_decimal_point (void); cairo_private double _cairo_strtod (const char *nptr, char **endptr); +#ifdef HAVE_STRNDUP +#define _cairo_strndup strndup +#else +cairo_private char * +_cairo_strndup (const char *s, size_t n); +#endif + /* cairo-path-fixed.c */ cairo_private cairo_path_fixed_t * _cairo_path_fixed_create (void); @@ -1285,13 +1299,14 @@ _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, cairo_private void _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, - cairo_surface_t *recording_surface); + cairo_surface_t *recording_surface, + const cairo_color_t *foreground_color); cairo_private void _cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_image_surface_t *surface, - cairo_bool_t uses_foreground_color); + const cairo_color_t *foreground_color); cairo_private cairo_int_status_t _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, @@ -1475,6 +1490,11 @@ _cairo_surface_tag (cairo_surface_t *surface, const char *tag_name, const char *attributes); +cairo_private cairo_bool_t +_cairo_surface_supports_color_glyph (cairo_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index); + cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, @@ -1848,6 +1868,12 @@ _cairo_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix); cairo_private void _cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect); +cairo_private const char * +_cairo_debug_operator_to_string (cairo_operator_t op); + +cairo_private const char * +_cairo_debug_status_to_string (cairo_int_status_t status); + cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, const cairo_polygon_t *polygon, @@ -1939,17 +1965,26 @@ cairo_private cairo_status_t _cairo_fopen (const char *filename, const char *mode, FILE **file_out); /* Avoid unnecessary PLT entries. */ +slim_hidden_proto (cairo_append_path); +slim_hidden_proto (cairo_arc); +slim_hidden_proto (cairo_arc_negative); +slim_hidden_proto (cairo_clip); +slim_hidden_proto (cairo_clip_extents); slim_hidden_proto (cairo_clip_preserve); slim_hidden_proto (cairo_close_path); +slim_hidden_proto (cairo_copy_path); slim_hidden_proto (cairo_create); slim_hidden_proto (cairo_curve_to); slim_hidden_proto (cairo_destroy); +slim_hidden_proto (cairo_device_to_user); +slim_hidden_proto (cairo_fill); slim_hidden_proto (cairo_fill_preserve); slim_hidden_proto (cairo_font_face_destroy); slim_hidden_proto (cairo_font_face_get_user_data); slim_hidden_proto_no_warn (cairo_font_face_reference); slim_hidden_proto (cairo_font_face_set_user_data); slim_hidden_proto (cairo_font_options_equal); +slim_hidden_proto (cairo_font_options_get_custom_palette_color); slim_hidden_proto (cairo_font_options_hash); slim_hidden_proto (cairo_font_options_merge); slim_hidden_proto (cairo_font_options_set_antialias); @@ -1959,14 +1994,17 @@ 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_hairline); +slim_hidden_proto (cairo_get_line_width); slim_hidden_proto (cairo_get_matrix); slim_hidden_proto (cairo_get_scaled_font); +slim_hidden_proto (cairo_get_source); slim_hidden_proto (cairo_get_target); slim_hidden_proto (cairo_get_tolerance); slim_hidden_proto (cairo_glyph_allocate); slim_hidden_proto (cairo_glyph_free); +slim_hidden_proto (cairo_has_current_point); +slim_hidden_proto (cairo_identity_matrix); slim_hidden_proto (cairo_image_surface_create); slim_hidden_proto (cairo_image_surface_create_for_data); slim_hidden_proto (cairo_image_surface_get_data); @@ -1983,35 +2021,73 @@ slim_hidden_proto (cairo_matrix_init_scale); slim_hidden_proto (cairo_matrix_init_translate); slim_hidden_proto (cairo_matrix_invert); slim_hidden_proto (cairo_matrix_multiply); +slim_hidden_proto (cairo_matrix_rotate); slim_hidden_proto (cairo_matrix_scale); slim_hidden_proto (cairo_matrix_transform_distance); slim_hidden_proto (cairo_matrix_transform_point); slim_hidden_proto (cairo_matrix_translate); +slim_hidden_proto (cairo_mesh_pattern_begin_patch); +slim_hidden_proto (cairo_mesh_pattern_curve_to); +slim_hidden_proto (cairo_mesh_pattern_end_patch); +slim_hidden_proto (cairo_mesh_pattern_get_control_point); +slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba); +slim_hidden_proto (cairo_mesh_pattern_get_patch_count); +slim_hidden_proto (cairo_mesh_pattern_get_path); +slim_hidden_proto (cairo_mesh_pattern_line_to); +slim_hidden_proto (cairo_mesh_pattern_move_to); +slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba); slim_hidden_proto (cairo_move_to); slim_hidden_proto (cairo_new_path); slim_hidden_proto (cairo_paint); +slim_hidden_proto (cairo_paint_with_alpha); +slim_hidden_proto_no_warn (cairo_path_destroy); slim_hidden_proto (cairo_pattern_add_color_stop_rgba); slim_hidden_proto (cairo_pattern_create_for_surface); +slim_hidden_proto (cairo_pattern_create_linear); +slim_hidden_proto (cairo_pattern_create_mesh); +slim_hidden_proto (cairo_pattern_create_radial); 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_mesh_pattern_curve_to); -slim_hidden_proto (cairo_mesh_pattern_get_control_point); -slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba); -slim_hidden_proto (cairo_mesh_pattern_get_patch_count); -slim_hidden_proto (cairo_mesh_pattern_get_path); -slim_hidden_proto (cairo_mesh_pattern_line_to); -slim_hidden_proto (cairo_mesh_pattern_move_to); -slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba); +slim_hidden_proto (cairo_pattern_get_rgba); +slim_hidden_proto (cairo_pattern_get_type); slim_hidden_proto_no_warn (cairo_pattern_reference); +slim_hidden_proto (cairo_pattern_set_extend); slim_hidden_proto (cairo_pattern_set_matrix); 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_no_warn (cairo_path_destroy); slim_hidden_proto (cairo_recording_surface_create); +slim_hidden_proto (cairo_recording_surface_ink_extents); +slim_hidden_proto (cairo_rectangle); +slim_hidden_proto (cairo_region_contains_point); +slim_hidden_proto (cairo_region_contains_rectangle); +slim_hidden_proto (cairo_region_copy); +slim_hidden_proto (cairo_region_create); +slim_hidden_proto (cairo_region_create_rectangle); +slim_hidden_proto (cairo_region_create_rectangles); +slim_hidden_proto (cairo_region_destroy); +slim_hidden_proto (cairo_region_equal); +slim_hidden_proto (cairo_region_get_extents); +slim_hidden_proto (cairo_region_get_rectangle); +slim_hidden_proto (cairo_region_intersect); +slim_hidden_proto (cairo_region_intersect_rectangle); +slim_hidden_proto (cairo_region_is_empty); +slim_hidden_proto (cairo_region_num_rectangles); +slim_hidden_proto (cairo_region_reference); +slim_hidden_proto (cairo_region_status); +slim_hidden_proto (cairo_region_subtract); +slim_hidden_proto (cairo_region_subtract_rectangle); +slim_hidden_proto (cairo_region_translate); +slim_hidden_proto (cairo_region_union); +slim_hidden_proto (cairo_region_union_rectangle); +slim_hidden_proto (cairo_region_xor); +slim_hidden_proto (cairo_region_xor_rectangle); slim_hidden_proto (cairo_rel_line_to); slim_hidden_proto (cairo_restore); +slim_hidden_proto (cairo_rotate); slim_hidden_proto (cairo_save); slim_hidden_proto (cairo_scale); slim_hidden_proto (cairo_scaled_font_create); @@ -2021,23 +2097,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_get_user_data); 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_scaled_font_get_user_data); slim_hidden_proto (cairo_scaled_font_set_user_data); +slim_hidden_proto (cairo_scaled_font_status); slim_hidden_proto (cairo_scaled_font_text_to_glyphs); +slim_hidden_proto (cairo_set_dash); +slim_hidden_proto (cairo_set_fill_rule); slim_hidden_proto (cairo_set_font_matrix); slim_hidden_proto (cairo_set_font_options); slim_hidden_proto (cairo_set_font_size); +slim_hidden_proto (cairo_set_hairline); 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_hairline); slim_hidden_proto (cairo_set_matrix); +slim_hidden_proto (cairo_set_miter_limit); slim_hidden_proto (cairo_set_operator); slim_hidden_proto (cairo_set_source); slim_hidden_proto (cairo_set_source_rgb); +slim_hidden_proto (cairo_set_source_rgba); slim_hidden_proto (cairo_set_source_surface); slim_hidden_proto (cairo_set_tolerance); slim_hidden_proto (cairo_status); @@ -2068,43 +2148,20 @@ 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_translate); 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_color_glyph_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_device_to_user); 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_create_rectangles); -slim_hidden_proto (cairo_region_copy); -slim_hidden_proto (cairo_region_reference); -slim_hidden_proto (cairo_region_destroy); -slim_hidden_proto (cairo_region_equal); -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); -slim_hidden_proto (cairo_region_xor); -slim_hidden_proto (cairo_region_xor_rectangle); #if CAIRO_HAS_PNG_FUNCTIONS +slim_hidden_proto (cairo_image_surface_create_from_png_stream); slim_hidden_proto (cairo_surface_write_to_png_stream); #endif diff --git a/src/check-def.sh b/src/check-def.sh deleted file mode 100755 index beefb46a3..000000000 --- a/src/check-def.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh - -LC_ALL=C -export LC_ALL - -if which nm 2>/dev/null >/dev/null; then - : -else - echo "'nm' not found; skipping test" - exit 0 -fi - -test -z "$srcdir" && srcdir=. -test -z "$MAKE" && MAKE=make -stat=0 - -$MAKE check-has-hidden-symbols.i > /dev/null || exit 1 -if tail -1 check-has-hidden-symbols.i | grep CAIRO_HAS_HIDDEN_SYMBOLS >/dev/null; then - echo "Compiler doesn't support symbol visibility; skipping test" - exit 0 -fi - -if [ "`uname -s`" = "Linux" ]; then - get_cairo_syms='( objdump -t "$so" | grep "^[^ ]* [^l.*]*[.]"; objdump -t "$so" | grep "[.]hidden.*\\<cairo"; ) | sed "s/.* //"' -else - get_cairo_syms='nm "$so" | grep " [BCDGINRSTVW] " | cut -d" " -f3' -fi - -defs="cairo.def" -$MAKE $defs > /dev/null -for def in $defs; do - lib=`echo "$def" | sed 's/[.]def$//'` - lib=`echo "$lib" | sed 's@.*/@@'` - so=.libs/lib${lib}.so - - test -f "$so" || continue - - echo Checking that $so has the same symbol list as $def - - { - echo EXPORTS - eval $get_cairo_syms | c++filt --no-params | grep -v '^_cairo_test_\|^_fini\|^_init\|^_save[fg]pr\|^_rest[fg]pr\|^_Z\|^__gnu\|^__bss\|^_edata\|^_end' | sort -u - # cheat: copy the last line from the def file! - tail -n1 "$def" - } | diff "$def" - >&2 || stat=1 -done - -exit $stat diff --git a/src/make-cairo-def.sh b/src/make-cairo-def.sh new file mode 100644 index 000000000..1a1f366e8 --- /dev/null +++ b/src/make-cairo-def.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +if [ $# -lt 3 ]; +then + echo "Generate cairo def file" + echo "Usage: $0 <def-filename> <cairo-features-file> <cairo-headers>..." + exit 1 +fi + +def_file="$1" +cairo_features_h="$2" +shift 2 + +#echo Generating $def_file + +(echo EXPORTS; \ + (cat $* || echo 'cairo_ERROR ()' ) | \ + egrep -v '^# *include' | \ + ( cat "$cairo_features_h" - | egrep -v '^#pragma' | cpp -D__cplusplus - || echo 'cairo_ERROR ()' ) | \ + egrep '^cairo_.* \(' | \ + sed -e 's/[ ].*//' | \ + sort; \ + ) > "$def_file" +grep -q -v cairo_ERROR "$def_file" || (rm "$def_file"; false) diff --git a/src/meson-check-def.sh b/src/meson-check-def.sh new file mode 100644 index 000000000..550cf337f --- /dev/null +++ b/src/meson-check-def.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +if [ $# -lt 2 ]; +then + echo "Check that cairo library has same exported symbols as cairo.def" + echo "Usage: $0 <def-filename> <cairo-library>" + exit 1 +fi + +def="$1" +so="$2" + +if which nm 2>/dev/null >/dev/null; then + : +else + echo "'nm' not found; skipping test" + exit 0 +fi + +stat=0 + +if [ "`uname -s`" = "Linux" ]; then + get_cairo_syms='( objdump -t "$so" | grep "^[^ ]* [^l.*]*[.]"; objdump -t "$so" | grep "[.]hidden.*\\<cairo"; ) | sed "s/.* //"' +else + get_cairo_syms='nm "$so" | grep " [BCDGINRSTVW] " | cut -d" " -f3' +fi + +echo Checking that $so has the same symbol list as $def + +{ + echo EXPORTS + eval $get_cairo_syms | c++filt --no-params | grep -v '^_cairo_test_\|^_fini\|^_init\|^_save[fg]pr\|^_rest[fg]pr\|^_Z\|^__gnu\|^__bss\|^_edata\|^_end' | sort -u +} | diff "$def" - >&2 || stat=1 + +exit $stat diff --git a/src/meson.build b/src/meson.build index 3d50edd54..f777fcd1c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -130,6 +130,8 @@ cairo_feature_sources = { ], 'cairo-ft': [ 'cairo-ft-font.c', + 'cairo-colr-glyph-render.c', + 'cairo-svg-glyph-render.c' ], 'cairo-xlib': [ @@ -177,25 +179,9 @@ cairo_feature_sources = { 'cairo-win32-font': [ 'win32/cairo-win32-font.c', ], - 'cairo-win32-dwrite-font': [ - 'win32/cairo-dwrite-font-public.c', + 'cairo-dwrite-font': [ 'win32/cairo-dwrite-font.cpp', ], - 'cairo-gl': [ - 'cairo-gl-composite.c', - 'cairo-gl-device.c', - 'cairo-gl-dispatch.c', - 'cairo-gl-glyphs.c', - 'cairo-gl-gradient.c', - 'cairo-gl-info.c', - 'cairo-gl-msaa-compositor.c', - 'cairo-gl-operand.c', - 'cairo-gl-shaders.c', - 'cairo-gl-source.c', - 'cairo-gl-spans-compositor.c', - 'cairo-gl-surface.c', - 'cairo-gl-traps-compositor.c', - ], 'cairo-script': [ 'cairo-script-surface.c', ], @@ -209,18 +195,6 @@ cairo_feature_sources = { 'cairo-svg': [ 'cairo-svg-surface.c', ], - 'cairo-egl': [ - 'cairo-egl-context.c', - ], - 'cairo-glx': [ - 'cairo-glx-context.c', - ], - 'cairo-wgl': [ - 'cairo-wgl-context.c', - ], - 'cairo-xml': [ - 'cairo-xml-surface.c', - ], 'cairo-tee': [ 'cairo-tee-surface.c', ], @@ -237,10 +211,10 @@ cairo_feature_headers = { 'cairo-quartz': ['cairo-quartz.h'], 'cairo-quartz-image': ['cairo-quartz-image.h'], 'cairo-win32': ['cairo-win32.h'], + 'cairo-dwrite-font': ['cairo-dwrite.h'], 'cairo-gl': ['cairo-gl.h'], 'cairo-script': ['cairo-script.h'], 'cairo-tee': ['cairo-tee.h'], - 'cairo-xml': ['cairo-xml.h'], 'cairo-vg': ['cairo-vg.h'], } @@ -260,6 +234,12 @@ endforeach incsrc = include_directories('.') +cairo_static_args = [] +if get_option('default_library') == 'static' and host_machine.system() == 'windows' + cairo_static_args += ['-DCAIRO_WIN32_STATIC_BUILD'] + add_project_arguments('-DCAIRO_WIN32_STATIC_BUILD', language: 'c') +endif + libcairo = library('cairo', cairo_sources, dependencies: deps, c_args: cairo_no_warn_c_args + pthread_c_args, @@ -271,12 +251,8 @@ libcairo = library('cairo', cairo_sources, include_directories: incbase, ) -cairo_headers += [configure_file(output: 'cairo-features.h', configuration: feature_conf)] - -cairo_static_args = [] -if get_option('default_library') == 'static' and host_machine.system() == 'windows' - cairo_static_args += ['-DCAIRO_WIN32_STATIC_BUILD'] -endif +cairo_features_file = configure_file(output: 'cairo-features.h', configuration: feature_conf) +cairo_headers += [cairo_features_file] libcairo_dep = declare_dependency(link_with: libcairo, dependencies: deps, @@ -296,9 +272,6 @@ install_headers(cairo_headers, subdir: 'cairo') shell = find_program('sh', required: false) if shell.found() test_scripts = [ - # This script calls back into make to generate cairo.def - # TODO: Make this work, somehow - #'check-def.sh', 'check-doc-syntax.sh', 'check-headers.sh', 'check-preprocessor-syntax.sh', @@ -314,6 +287,21 @@ if shell.found() env = environment() env.set('CAIRO_HAS_HIDDEN_SYMBOLS', '1') + cairo_def = custom_target('make-cairo-def', + input : cairo_headers, + output : 'cairo.def', + command : [ shell, + meson.current_source_dir()/'make-cairo-def.sh', + '@OUTPUT@', + cairo_features_file, + '@INPUT@' + ]) + + test('check-def', shell, + args: ['meson-check-def.sh', cairo_def, libcairo ], + env: env, + workdir: meson.current_source_dir()) + test('check-plt.sh', shell, args: ['check-plt.sh', libcairo ], env: env, diff --git a/src/win32/cairo-dwrite-font-public.c b/src/win32/cairo-dwrite-font-public.c deleted file mode 100644 index 09eddd51d..000000000 --- a/src/win32/cairo-dwrite-font-public.c +++ /dev/null @@ -1,130 +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 © 2022 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> - */ - - -/* gtkdoc won't scan .cpp files so we wrap the public API in cairo-dwrite-font.cpp - * with this .c wrapper containing the gtkdocs for cairo-dwrite-font.cpp. - */ - -#include "cairoint.h" -#include "cairo-win32-private.h" - -/** - * SECTION:cairo-dwrite-fonts - * @Title: DWrite Fonts - * @Short_Description: Font support for Microsoft DirectWrite - * @See_Also: #cairo_font_face_t - * - * The Microsoft DirectWrite font backend is primarily used to render text on - * Microsoft Windows systems. - **/ - -/** - * CAIRO_HAS_DWRITE_FONT: - * - * Defined if the Microsoft DWrite font backend is available. - * This macro can be used to conditionally compile backend-specific code. - * - * Since: 1.18 - **/ - -/** - * cairo_dwrite_font_face_create_for_dwrite_fontface: - * @dwrite_font_face: A pointer to an #IDWriteFontFace specifying the - * DWrite font to use. - * - * Creates a new font for the DWrite font backend based on a - * DWrite font face. This font can then be used with - * cairo_set_font_face() or cairo_scaled_font_create(). - - * Here is an example of how this function might be used: - * <informalexample><programlisting><![CDATA[ - * #include <cairo-win32.h> - * #include <dwrite.h> - * - * IDWriteFactory* dWriteFactory = NULL; - * HRESULT hr = DWriteCreateFactory( - * DWRITE_FACTORY_TYPE_SHARED, - * __uuidof(IDWriteFactory), - * reinterpret_cast<IUnknown**>(&dWriteFactory)); - * - * IDWriteFontCollection *systemCollection; - * hr = dWriteFactory->GetSystemFontCollection(&systemCollection); - * - * UINT32 idx; - * BOOL found; - * systemCollection->FindFamilyName(L"Segoe UI Emoji", &idx, &found); - * - * IDWriteFontFamily *family; - * systemCollection->GetFontFamily(idx, &family); - * - * IDWriteFont *dwritefont; - * DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; - * DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; - * hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &dwritefont); - * - * IDWriteFontFace *dwriteface; - * hr = dwritefont->CreateFontFace(&dwriteface); - * - * cairo_font_face_t *face; - * face = cairo_dwrite_font_face_create_for_dwrite_fontface(dwriteface); - * cairo_set_font_face(cr, face); - * cairo_set_font_size(cr, 70); - * cairo_move_to(cr, 100, 100); - * cairo_show_text(cr, "😃"); - * ]]></programlisting></informalexample> - * - * Note: When printing a DWrite font to a - * #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface, the printing surface - * will substitute each DWrite font with a Win32 font created from the same - * underlying font file. If the matching font file can not be found, - * the #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface will convert each - * glyph to a filled path. If a DWrite font was not created from a system - * font, it is recommended that the font used to create the DWrite - * font be made available to GDI to avoid the undesirable fallback - * to emitting paths. This can be achieved using the GDI font loading functions - * such as AddFontMemResourceEx(). - * - * Return value: a newly created #cairo_font_face_t. Free with - * cairo_font_face_destroy() when you are done using it. - * - * Since: 1.18 - **/ -cairo_font_face_t* -cairo_dwrite_font_face_create_for_dwrite_fontface (void *dwrite_font_face) -{ - return cairo_dwrite_font_face_create_for_dwrite_fontface_internal (dwrite_font_face); -} diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index c58827555..cf516d41c 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -45,10 +45,31 @@ #include "cairo-dwrite-private.hpp" #include "cairo-truetype-subset-private.h" #include "cairo-scaled-font-subsets-private.h" +#include "cairo-dwrite.h" + #include <float.h> #include <wincodec.h> +/** + * SECTION:cairo-dwrite-fonts + * @Title: DWrite Fonts + * @Short_Description: Font support for Microsoft DirectWrite + * @See_Also: #cairo_font_face_t + * + * The Microsoft DirectWrite font backend is primarily used to render text on + * Microsoft Windows systems. + **/ + +/** + * CAIRO_HAS_DWRITE_FONT: + * + * Defined if the Microsoft DWrite font backend is available. + * This macro can be used to conditionally compile backend-specific code. + * + * Since: 1.18 + **/ + typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( D2D1_FACTORY_TYPE factoryType, REFIID iid, @@ -191,22 +212,119 @@ private: RefPtr<IDWriteFactory> DWriteFactory::mFactoryInstance; +RefPtr<IDWriteFactory1> DWriteFactory::mFactoryInstance1; +RefPtr<IDWriteFactory2> DWriteFactory::mFactoryInstance2; +RefPtr<IDWriteFactory3> DWriteFactory::mFactoryInstance3; RefPtr<IDWriteFactory4> DWriteFactory::mFactoryInstance4; RefPtr<IWICImagingFactory> WICImagingFactory::mFactoryInstance; RefPtr<IDWriteFontCollection> DWriteFactory::mSystemCollection; RefPtr<IDWriteRenderingParams> DWriteFactory::mDefaultRenderingParams; -RefPtr<IDWriteRenderingParams> DWriteFactory::mCustomClearTypeRenderingParams; -RefPtr<IDWriteRenderingParams> DWriteFactory::mForceGDIClassicRenderingParams; -FLOAT DWriteFactory::mGamma = -1.0; -FLOAT DWriteFactory::mEnhancedContrast = -1.0; -FLOAT DWriteFactory::mClearTypeLevel = -1.0; -int DWriteFactory::mPixelGeometry = -1; -int DWriteFactory::mRenderingMode = -1; RefPtr<ID2D1Factory> D2DFactory::mFactoryInstance; RefPtr<ID2D1DCRenderTarget> D2DFactory::mRenderTarget; +static int +_quality_from_antialias_mode(cairo_antialias_t antialias) +{ + switch (antialias) { + case CAIRO_ANTIALIAS_NONE: + return NONANTIALIASED_QUALITY; + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GRAY: + return ANTIALIASED_QUALITY; + default: + break; + } + return CLEARTYPE_QUALITY; +} + +static RefPtr<IDWriteRenderingParams> +_create_rendering_params(IDWriteRenderingParams *params, + const cairo_font_options_t *options, + cairo_antialias_t antialias) +{ + if (!params) + params = DWriteFactory::DefaultRenderingParams(); + FLOAT gamma = params->GetGamma(); + FLOAT enhanced_contrast = params->GetEnhancedContrast(); + FLOAT clear_type_level = params->GetClearTypeLevel(); + DWRITE_PIXEL_GEOMETRY pixel_geometry = params->GetPixelGeometry(); + DWRITE_RENDERING_MODE rendering_mode = params->GetRenderingMode(); + + cairo_bool_t modified = FALSE; + switch (antialias) { + case CAIRO_ANTIALIAS_NONE: + if (rendering_mode != DWRITE_RENDERING_MODE_ALIASED) { + rendering_mode = DWRITE_RENDERING_MODE_ALIASED; + modified = TRUE; + } + break; + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GRAY: + if (clear_type_level) { + clear_type_level = 0; + modified = TRUE; + } + break; + default: + break; + } + auto subpixel_order = cairo_font_options_get_subpixel_order (options); + switch (subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_RGB: + if (pixel_geometry != DWRITE_PIXEL_GEOMETRY_RGB) { + pixel_geometry = DWRITE_PIXEL_GEOMETRY_RGB; + modified = TRUE; + } + break; + case CAIRO_SUBPIXEL_ORDER_BGR: + if (pixel_geometry != DWRITE_PIXEL_GEOMETRY_BGR) { + pixel_geometry = DWRITE_PIXEL_GEOMETRY_BGR; + modified = TRUE; + } + break; + default: + break; + } + if (!modified) + return params; + + HRESULT hr; + RefPtr<IDWriteRenderingParams1> params1; + hr = params->QueryInterface(¶ms1); + if (FAILED(hr)) { + RefPtr<IDWriteRenderingParams> ret; + DWriteFactory::Instance()->CreateCustomRenderingParams(gamma, enhanced_contrast, clear_type_level, pixel_geometry, rendering_mode, &ret); + return ret; + } + + FLOAT grayscaleEnhancedContrast = params1->GetGrayscaleEnhancedContrast(); + RefPtr<IDWriteRenderingParams2> params2; + hr = params->QueryInterface(¶ms2); + if (FAILED(hr)) { + RefPtr<IDWriteRenderingParams1> ret; + DWriteFactory::Instance1()->CreateCustomRenderingParams(gamma, enhanced_contrast, grayscaleEnhancedContrast, clear_type_level, pixel_geometry, rendering_mode, &ret); + return ret; + } + + DWRITE_GRID_FIT_MODE gridFitMode = params2->GetGridFitMode(); + RefPtr<IDWriteRenderingParams3> params3; + hr = params->QueryInterface(¶ms3); + if (FAILED(hr)) { + RefPtr<IDWriteRenderingParams2> ret; + DWriteFactory::Instance2()->CreateCustomRenderingParams(gamma, enhanced_contrast, grayscaleEnhancedContrast, clear_type_level, pixel_geometry, rendering_mode, gridFitMode, &ret); + return ret; + } + + DWRITE_RENDERING_MODE1 rendering_mode1 = params3->GetRenderingMode1(); + if (antialias == CAIRO_ANTIALIAS_NONE) + rendering_mode1 = DWRITE_RENDERING_MODE1_ALIASED; + RefPtr<IDWriteRenderingParams3> ret; + DWriteFactory::Instance3()->CreateCustomRenderingParams(gamma, enhanced_contrast, grayscaleEnhancedContrast, clear_type_level, pixel_geometry, rendering_mode1, gridFitMode, &ret); + return ret; +} + /* Functions #cairo_font_face_backend_t */ static cairo_status_t _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, @@ -330,7 +448,7 @@ _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, face_name, face_name_len); RefPtr<IDWriteFontFamily> family = DWriteFactory::FindSystemFontFamily(face_name); - delete face_name; + delete[] face_name; if (!family) { /* If the family is not found, use the default that should always exist. */ face_name_len = MultiByteToWideChar(CP_UTF8, 0, CAIRO_FONT_FAMILY_DEFAULT, -1, NULL, 0); @@ -338,7 +456,7 @@ _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, MultiByteToWideChar(CP_UTF8, 0, CAIRO_FONT_FAMILY_DEFAULT, -1, face_name, face_name_len); family = DWriteFactory::FindSystemFontFamily(face_name); - delete face_name; + delete[] face_name; if (!family) { *font_face = (cairo_font_face_t*)&_cairo_font_face_nil; return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; @@ -390,6 +508,8 @@ _cairo_dwrite_font_face_destroy (void *font_face) cairo_dwrite_font_face_t *dwrite_font_face = static_cast<cairo_dwrite_font_face_t*>(font_face); if (dwrite_font_face->dwriteface) dwrite_font_face->dwriteface->Release(); + if (dwrite_font_face->rendering_params) + dwrite_font_face->rendering_params->Release(); return TRUE; } @@ -536,10 +656,21 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, return status; } + dwrite_font->mat = dwrite_font->base.ctm; + cairo_matrix_multiply(&dwrite_font->mat, &dwrite_font->mat, font_matrix); + dwrite_font->mat_inverse = dwrite_font->mat; + cairo_matrix_invert (&dwrite_font->mat_inverse); + cairo_font_extents_t extents; DWRITE_FONT_METRICS metrics; - font_face->dwriteface->GetMetrics(&metrics); + if (dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || + dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) { + DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&dwrite_font->mat); + font_face->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &metrics); + } else { + font_face->dwriteface->GetMetrics(&metrics); + } extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; extents.descent = (FLOAT)metrics.descent / metrics.designUnitsPerEm; @@ -547,15 +678,8 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, extents.max_x_advance = 14.0; extents.max_y_advance = 0.0; - dwrite_font->mat = dwrite_font->base.ctm; - cairo_matrix_multiply(&dwrite_font->mat, &dwrite_font->mat, font_matrix); - dwrite_font->mat_inverse = dwrite_font->mat; - cairo_matrix_invert (&dwrite_font->mat_inverse); - cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL; - dwrite_font->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - // The following code detects the system quality at scaled_font creation time, // this means that if cleartype settings are changed but the scaled_fonts // are re-used, they might not adhere to the new system setting until re- @@ -566,12 +690,10 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, break; case ANTIALIASED_QUALITY: default_quality = CAIRO_ANTIALIAS_GRAY; - dwrite_font->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; break; case DEFAULT_QUALITY: // _get_system_quality() seems to think aliased is default! default_quality = CAIRO_ANTIALIAS_NONE; - dwrite_font->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; break; } @@ -587,9 +709,8 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, dwrite_font->antialias_mode = options->antialias; } - dwrite_font->rendering_mode = - default_quality == CAIRO_ANTIALIAS_SUBPIXEL ? - cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL : cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; + dwrite_font->rendering_params = _create_rendering_params(font_face->rendering_params, options, dwrite_font->antialias_mode).forget().drop(); + dwrite_font->measuring_mode = font_face->measuring_mode; return _cairo_scaled_font_set_metrics (*font, &extents); } @@ -598,6 +719,9 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, static void _cairo_dwrite_scaled_font_fini(void *scaled_font) { + cairo_dwrite_scaled_font_t *dwrite_font = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font); + if (dwrite_font->rendering_params) + dwrite_font->rendering_params->Release(); } static cairo_int_status_t @@ -659,17 +783,30 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_ DWRITE_GLYPH_METRICS metrics; DWRITE_FONT_METRICS fontMetrics; - font_face->dwriteface->GetMetrics(&fontMetrics); - HRESULT hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); + HRESULT hr; + if (font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || + font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) { + DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&scaled_font->mat); + font_face->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &fontMetrics); + BOOL natural = font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL; + hr = font_face->dwriteface->GetGdiCompatibleGlyphMetrics (1, 1, &transform, natural, &charIndex, 1, &metrics, FALSE); + } else { + font_face->dwriteface->GetMetrics(&fontMetrics); + hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); + } if (FAILED(hr)) { return CAIRO_INT_STATUS_UNSUPPORTED; } + // GetGdiCompatibleMetrics may return a glyph metrics that yields a small nagative glyph height. + INT32 glyph_width = metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing; + INT32 glyph_height = metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing; + glyph_width = MAX(glyph_width, 0); + glyph_height = MAX(glyph_height, 0); + // TODO: Treat swap_xy. - extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) / - fontMetrics.designUnitsPerEm; - extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) / - fontMetrics.designUnitsPerEm; + extents.width = (FLOAT)glyph_width / fontMetrics.designUnitsPerEm; + extents.height = (FLOAT)glyph_height / fontMetrics.designUnitsPerEm; extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; extents.y_advance = 0.0; @@ -679,10 +816,15 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_ // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, // and therefore it does not always cover all pixels that will actually be touched. - if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE && - extents.width > 0 && extents.height > 0) { - extents.width += scaled_font->mat_inverse.xx * 2; - extents.x_bearing -= scaled_font->mat_inverse.xx; + if (extents.width > 0 && extents.height > 0) { + double x = 1, y = 1; + cairo_matrix_transform_distance (&scaled_font->mat_inverse, &x, &y); + x = fabs(x); + y = fabs(y); + extents.width += x * 2; + extents.x_bearing -= x; + extents.height += y * 2; + extents.y_bearing -= y; } _cairo_scaled_glyph_set_metrics (scaled_glyph, @@ -699,8 +841,9 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_ class GeometryRecorder : public IDWriteGeometrySink { public: - GeometryRecorder(cairo_path_fixed_t *aCairoPath) - : mCairoPath(aCairoPath) {} + GeometryRecorder(cairo_path_fixed_t *aCairoPath, const cairo_matrix_t &matrix) + : mCairoPath(aCairoPath) + , mMatrix(matrix) {} // IUnknown interface IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) @@ -738,28 +881,18 @@ public: return; } - cairo_fixed_t GetFixedX(const D2D1_POINT_2F &point) - { - unsigned int control_word; - _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); - return _cairo_fixed_from_double(point.x); - } - - cairo_fixed_t GetFixedY(const D2D1_POINT_2F &point) - { - unsigned int control_word; - _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); - return _cairo_fixed_from_double(point.y); - } - IFACEMETHODIMP_(void) BeginFigure( D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) { - mStartPoint = startPoint; + double x = startPoint.x; + double y = startPoint.y; + cairo_matrix_transform_point(&mMatrix, &x, &y); + mStartPointX = _cairo_fixed_from_double(x); + mStartPointY = _cairo_fixed_from_double(y); cairo_status_t status = _cairo_path_fixed_move_to(mCairoPath, - GetFixedX(startPoint), - GetFixedY(startPoint)); + mStartPointX, + mStartPointY); (void)status; /* squelch warning */ } @@ -768,8 +901,8 @@ public: { if (figureEnd == D2D1_FIGURE_END_CLOSED) { cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, - GetFixedX(mStartPoint), - GetFixedY(mStartPoint)); + mStartPointX, + mStartPointY); (void)status; /* squelch warning */ } } @@ -779,13 +912,22 @@ public: UINT beziersCount) { for (unsigned int i = 0; i < beziersCount; i++) { + double x1 = beziers[i].point1.x; + double y1 = beziers[i].point1.y; + double x2 = beziers[i].point2.x; + double y2 = beziers[i].point2.y; + double x3 = beziers[i].point3.x; + double y3 = beziers[i].point3.y; + cairo_matrix_transform_point(&mMatrix, &x1, &y1); + cairo_matrix_transform_point(&mMatrix, &x2, &y2); + cairo_matrix_transform_point(&mMatrix, &x3, &y3); cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, - GetFixedX(beziers[i].point1), - GetFixedY(beziers[i].point1), - GetFixedX(beziers[i].point2), - GetFixedY(beziers[i].point2), - GetFixedX(beziers[i].point3), - GetFixedY(beziers[i].point3)); + _cairo_fixed_from_double(x1), + _cairo_fixed_from_double(y1), + _cairo_fixed_from_double(x2), + _cairo_fixed_from_double(y2), + _cairo_fixed_from_double(x3), + _cairo_fixed_from_double(y3)); (void)status; /* squelch warning */ } } @@ -795,16 +937,21 @@ public: UINT pointsCount) { for (unsigned int i = 0; i < pointsCount; i++) { + double x = points[i].x; + double y = points[i].y; + cairo_matrix_transform_point(&mMatrix, &x, &y); cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, - GetFixedX(points[i]), - GetFixedY(points[i])); + _cairo_fixed_from_double(x), + _cairo_fixed_from_double(y)); (void)status; /* squelch warning */ } } private: cairo_path_fixed_t *mCairoPath; - D2D1_POINT_2F mStartPoint; + const cairo_matrix_t &mMatrix; + cairo_fixed_t mStartPointX; + cairo_fixed_t mStartPointY; }; static cairo_int_status_t @@ -814,7 +961,7 @@ _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_fon cairo_int_status_t status; cairo_path_fixed_t *path; path = _cairo_path_fixed_create(); - GeometryRecorder recorder(path); + GeometryRecorder recorder(path, scaled_font->base.scale); DWRITE_GLYPH_OFFSET offset; offset.advanceOffset = 0; @@ -823,12 +970,7 @@ _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_fon FLOAT advance = 0.0; cairo_dwrite_font_face_t *dwriteff = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; - /* GetGlyphRunOutline seems to ignore hinting so just use the em size to get the outline - * to avoid rounding errors when converting to cairo_path_fixed_t. - */ - DWRITE_FONT_METRICS metrics; - dwriteff->dwriteface->GetMetrics(&metrics); - HRESULT hr = dwriteff->dwriteface->GetGlyphRunOutline(metrics.designUnitsPerEm, + HRESULT hr = dwriteff->dwriteface->GetGlyphRunOutline(1, &glyphId, &advance, &offset, @@ -841,12 +983,6 @@ _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_fon status = (cairo_int_status_t)_cairo_path_fixed_close_path(path); - /* Now scale the em size down to 1.0 and apply the font matrix and font ctm. */ - cairo_matrix_t mat = scaled_font->base.ctm; - cairo_matrix_multiply(&mat, &scaled_font->base.font_matrix, &mat); - cairo_matrix_scale (&mat, 1.0/metrics.designUnitsPerEm, 1.0/metrics.designUnitsPerEm); - _cairo_path_fixed_transform(path, &mat); - _cairo_scaled_glyph_set_path (scaled_glyph, &scaled_font->base, path); @@ -860,7 +996,6 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s { int width, height; double x1, y1, x2, y2; - cairo_glyph_t glyph; cairo_bool_t uses_foreground_color = FALSE; cairo_dwrite_font_face_t *dwrite_font_face = (cairo_dwrite_font_face_t *)scaled_font->base.font_face; @@ -877,22 +1012,18 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s width = (int)(x2 - x1); height = (int)(y2 - y1); - glyph.index = _cairo_scaled_glyph_index (scaled_glyph); - glyph.x = x1; - glyph.y = y1; - DWRITE_GLYPH_RUN run; FLOAT advance = 0; - UINT16 index = (UINT16)glyph.index; + UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; - double x = -glyph.x; - double y = -glyph.y; + double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); + double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); DWRITE_MATRIX matrix; D2D1_POINT_2F origin = {0, 0}; RefPtr<IDWriteColorGlyphRunEnumerator1> run_enumerator; HRESULT hr; - /** + /* * We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the @@ -901,7 +1032,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s */ cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); offset.advanceOffset = (FLOAT)x; - /** Y-axis is inverted */ + /* Y-axis is inverted */ offset.ascenderOffset = -(FLOAT)y; run.fontFace = dwrite_font_face->dwriteface; @@ -938,7 +1069,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s &run, NULL, /* glyphRunDescription */ supported_formats, - DWRITE_MEASURING_MODE_NATURAL, + dwrite_font_face->measuring_mode, &matrix, palette_index, &run_enumerator); @@ -1013,8 +1144,8 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s if (FAILED(hr) || !have_run) break; - DWRITE_COLOR_GLYPH_RUN1 const* color_run; - hr = run_enumerator->GetCurrentRun(&color_run); + DWRITE_COLOR_GLYPH_RUN1_WORKAROUND const* color_run; + hr = run_enumerator->GetCurrentRun(reinterpret_cast<const DWRITE_COLOR_GLYPH_RUN1**>(&color_run)); if (FAILED(hr)) return _cairo_dwrite_error (hr, "GetCurrentRun failed"); @@ -1027,7 +1158,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s dc4->DrawColorBitmapGlyphRun(color_run->glyphImageFormat, origin, &color_run->glyphRun, - DWRITE_MEASURING_MODE_NATURAL, + dwrite_font_face->measuring_mode, D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT); break; @@ -1038,7 +1169,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s foreground_color_brush, nullptr, palette_index, - DWRITE_MEASURING_MODE_NATURAL); + dwrite_font_face->measuring_mode); uses_foreground_color = TRUE; break; case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE: @@ -1050,14 +1181,23 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s color_brush->SetColor(&color); uses_foreground_color = TRUE; } else { - color_brush->SetColor(color_run->runColor); + double red, green, blue, alpha; + cairo_status_t status; + status = cairo_font_options_get_custom_palette_color (&scaled_font->base.options, + color_run->paletteIndex, + &red, &blue, &green, &alpha); + if (status == CAIRO_STATUS_SUCCESS) { + color_brush->SetColor(D2D1::ColorF(red, blue, green, alpha)); + } else { + color_brush->SetColor(color_run->runColor); + } } dc4->DrawGlyphRun(origin, &color_run->glyphRun, color_run->glyphRunDescription, color_brush, - DWRITE_MEASURING_MODE_NATURAL); + dwrite_font_face->measuring_mode); case DWRITE_GLYPH_IMAGE_FORMATS_NONE: break; } @@ -1079,55 +1219,18 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s _cairo_scaled_glyph_set_color_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t *) image, - uses_foreground_color); + uses_foreground_color ? foreground_color : NULL); scaled_glyph->color_glyph = TRUE; scaled_glyph->color_glyph_set = TRUE; return CAIRO_INT_STATUS_SUCCESS; } -/* Helper function adapted from _compute_mask in cairo-win32-font.c */ - -/* Compute an alpha-mask from a monochrome RGB24 image - */ -static cairo_surface_t * -_compute_a8_mask (cairo_surface_t *surface) -{ - cairo_image_surface_t *glyph; - cairo_image_surface_t *mask; - int i, j; - - glyph = (cairo_image_surface_t *)cairo_surface_map_to_image (surface, NULL); - if (unlikely (glyph->base.status)) - return &glyph->base; - - /* No quality param, just use the non-ClearType path */ - - /* Compute an alpha-mask by using the green channel of a (presumed monochrome) - * RGB24 image. - */ - mask = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_A8, glyph->width, glyph->height); - if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { - for (i = 0; i < glyph->height; i++) { - uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride); - uint8_t *q = (uint8_t *) (mask->data + i * mask->stride); - - for (j = 0; j < glyph->width; j++) - *q++ = 255 - ((*p++ & 0x0000ff00) >> 8); - } - } - - cairo_surface_unmap_image (surface, &glyph->base); - return &mask->base; -} - static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_int_status_t status; - cairo_glyph_t glyph; cairo_win32_surface_t *surface; cairo_t *cr; cairo_surface_t *image; @@ -1141,16 +1244,12 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_ width = (int)(x2 - x1); height = (int)(y2 - y1); - glyph.index = _cairo_scaled_glyph_index (scaled_glyph); - glyph.x = -x1; - glyph.y = -y1; - DWRITE_GLYPH_RUN run; FLOAT advance = 0; - UINT16 index = (UINT16)glyph.index; + UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; - double x = glyph.x; - double y = glyph.y; + double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); + double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); RECT area; DWRITE_MATRIX matrix; @@ -1165,7 +1264,7 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_ if (status) goto FAIL; - /** + /* * We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the @@ -1174,7 +1273,7 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_ */ cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); offset.advanceOffset = (FLOAT)x; - /** Y-axis is inverted */ + /* Y-axis is inverted */ offset.ascenderOffset = -(FLOAT)y; area.top = 0; @@ -1200,7 +1299,7 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_ GdiFlush(); - image = _compute_a8_mask (&surface->base); + image = _cairo_compute_glyph_mask (&surface->base, _quality_from_antialias_mode(scaled_font->antialias_mode)); status = (cairo_int_status_t)image->status; if (status) goto FAIL; @@ -1336,8 +1435,70 @@ _cairo_dwrite_has_color_glyphs(void *scaled_font) return ((cairo_dwrite_font_face_t *)dwritesf->base.font_face)->have_color; } -cairo_font_face_t* -cairo_dwrite_font_face_create_for_dwrite_fontface_internal(void* dwrite_font_face) +/** + * cairo_dwrite_font_face_create_for_dwrite_fontface: + * @dwrite_font_face: A pointer to an #IDWriteFontFace specifying the + * DWrite font to use. + * + * Creates a new font for the DWrite font backend based on a + * DWrite font face. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * + * Here is an example of how this function might be used: + * <informalexample><programlisting><![CDATA[ + * #include <cairo-dwrite.h> + * #include <dwrite.h> + * + * IDWriteFactory* dWriteFactory = NULL; + * HRESULT hr = DWriteCreateFactory( + * DWRITE_FACTORY_TYPE_SHARED, + * __uuidof(IDWriteFactory), + * reinterpret_cast<IUnknown**>(&dWriteFactory)); + * + * IDWriteFontCollection *systemCollection; + * hr = dWriteFactory->GetSystemFontCollection(&systemCollection); + * + * UINT32 idx; + * BOOL found; + * systemCollection->FindFamilyName(L"Segoe UI Emoji", &idx, &found); + * + * IDWriteFontFamily *family; + * systemCollection->GetFontFamily(idx, &family); + * + * IDWriteFont *dwritefont; + * DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; + * DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; + * hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &dwritefont); + * + * IDWriteFontFace *dwriteface; + * hr = dwritefont->CreateFontFace(&dwriteface); + * + * cairo_font_face_t *face; + * face = cairo_dwrite_font_face_create_for_dwrite_fontface(dwriteface); + * cairo_set_font_face(cr, face); + * cairo_set_font_size(cr, 70); + * cairo_move_to(cr, 100, 100); + * cairo_show_text(cr, "😃"); + * ]]></programlisting></informalexample> + * + * Note: When printing a DWrite font to a + * #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface, the printing surface + * will substitute each DWrite font with a Win32 font created from the same + * underlying font file. If the matching font file can not be found, + * the #CAIRO_SURFACE_TYPE_WIN32_PRINTING surface will convert each + * glyph to a filled path. If a DWrite font was not created from a system + * font, it is recommended that the font used to create the DWrite + * font be made available to GDI to avoid the undesirable fallback + * to emitting paths. This can be achieved using the GDI font loading functions + * such as AddFontMemResourceEx(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.18 + **/ +cairo_font_face_t * +cairo_dwrite_font_face_create_for_dwrite_fontface (IDWriteFontFace *dwrite_font_face) { IDWriteFontFace *dwriteface = static_cast<IDWriteFontFace*>(dwrite_font_face); // Must do malloc and not C++ new, since Cairo frees this. @@ -1350,6 +1511,8 @@ cairo_dwrite_font_face_create_for_dwrite_fontface_internal(void* dwrite_font_fac dwriteface->AddRef(); face->dwriteface = dwriteface; face->have_color = false; + face->rendering_params = NULL; + face->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; /* Ensure IDWriteFactory4 is available before enabling color fonts */ if (DWriteFactory::Instance4()) { @@ -1367,35 +1530,77 @@ cairo_dwrite_font_face_create_for_dwrite_fontface_internal(void* dwrite_font_fac return font_face; } -void -cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force) +/** + * cairo_dwrite_font_face_get_rendering_params: + * @font_face: The #cairo_dwrite_font_face_t object to query + * + * Gets the #IDWriteRenderingParams object of @font_face. + * + * Return value: the #IDWriteRenderingParams object or %NULL if none. + * + * Since: 1.18 + **/ +IDWriteRenderingParams * +cairo_dwrite_font_face_get_rendering_params (cairo_font_face_t *font_face) { - cairo_dwrite_scaled_font_t *font = reinterpret_cast<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font); - if (force && font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL) { - font->rendering_mode = cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC; - } else if (!force && font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC) { - font->rendering_mode = cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL; - } + cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); + return dwface->rendering_params; } -cairo_bool_t -cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font) +/** + * cairo_dwrite_font_face_set_rendering_params: + * @font_face: The #cairo_dwrite_font_face_t object to modify + * @params: The #IDWriteRenderingParams object + * + * Sets the #IDWriteRenderingParams object to @font_face. + * This #IDWriteRenderingParams is used to render glyphs if default values of font options are used. + * If non-defalut values of font options are specified when creating a #cairo_scaled_font_t, + * cairo creates a new #IDWriteRenderingParams object for the #cairo_scaled_font_t object by overwriting the corresponding parameters. + * + * Since: 1.18 + **/ +void +cairo_dwrite_font_face_set_rendering_params (cairo_font_face_t *font_face, IDWriteRenderingParams *params) { - cairo_dwrite_scaled_font_t *font = reinterpret_cast<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font); - return font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC; + cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); + if (dwface->rendering_params) + dwface->rendering_params->Release(); + dwface->rendering_params = params; + if (dwface->rendering_params) + dwface->rendering_params->AddRef(); } -void -cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, - int geometry, int mode) +/** + * cairo_dwrite_font_face_get_measuring_mode: + * @font_face: The #cairo_dwrite_font_face_t object to query + * + * Gets the #DWRITE_MEASURING_MODE enum of @font_face. + * + * Return value: The #DWRITE_MEASURING_MODE enum of @font_face. + * + * Since: 1.18 + **/ +DWRITE_MEASURING_MODE +cairo_dwrite_font_face_get_measuring_mode (cairo_font_face_t *font_face) { - DWriteFactory::SetRenderingParams(gamma, contrast, level, geometry, mode); + cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); + return dwface->measuring_mode; } -int -cairo_dwrite_get_cleartype_rendering_mode() +/** + * cairo_dwrite_font_face_set_measuring_mode: + * @font_face: The #cairo_dwrite_font_face_t object to modify + * @mode: The #DWRITE_MEASURING_MODE enum. + * + * Sets the #DWRITE_MEASURING_MODE enum to @font_face. + * + * Since: 1.18 + **/ +void +cairo_dwrite_font_face_set_measuring_mode (cairo_font_face_t *font_face, DWRITE_MEASURING_MODE mode) { - return DWriteFactory::GetClearTypeRenderingMode(); + cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t *>(font_face); + dwface->measuring_mode = mode; } static cairo_int_status_t @@ -1411,9 +1616,6 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, RefPtr<IDWriteBitmapRenderTarget> rt; HRESULT hr; - cairo_dwrite_scaled_font_t::TextRenderingState renderingState = - scaled_font->rendering_mode; - hr = gdiInterop->CreateBitmapRenderTarget(surface->dc, area.right - area.left, area.bottom - area.top, @@ -1427,22 +1629,7 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, } } - if ((renderingState == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL || - renderingState == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC) - /* && !surface->base.permit_subpixel_antialiasing */ ) { - renderingState = cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; - RefPtr<IDWriteBitmapRenderTarget1> rt1; - hr = rt->QueryInterface(&rt1); - - if (SUCCEEDED(hr) && rt1) { - rt1->SetTextAntialiasMode(DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE); - } - } - - RefPtr<IDWriteRenderingParams> params = - DWriteFactory::RenderingParams(renderingState); - - /** + /* * We set the number of pixels per DIP to 1.0. This is because we always want * to draw in device pixels, and not device independent pixels. On high DPI * systems this value will be higher than 1.0 and automatically upscale @@ -1450,8 +1637,15 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, */ rt->SetPixelsPerDip(1.0); + float x = 0, y = 0; if (transform) { - rt->SetCurrentTransform(transform); + DWRITE_MATRIX matrix = *transform; + matrix.dx -= area.left; + matrix.dy -= area.top; + rt->SetCurrentTransform(&matrix); + } else { + x = (float) -area.left; + y = (float) -area.top; } BitBlt(rt->GetMemoryDC(), 0, 0, @@ -1459,17 +1653,7 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, surface->dc, area.left, area.top, SRCCOPY | NOMIRRORBITMAP); - DWRITE_MEASURING_MODE measureMode; - switch (renderingState) { - case cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC: - case cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE: - measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; - break; - default: - measureMode = DWRITE_MEASURING_MODE_NATURAL; - break; - } - rt->DrawGlyphRun(0, 0, measureMode, run, params, color); + rt->DrawGlyphRun(x, y, scaled_font->measuring_mode, run, scaled_font->rendering_params, color); BitBlt(surface->dc, area.left, area.top, area.right - area.left, area.bottom - area.top, @@ -1505,6 +1689,7 @@ _dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED; + float x = 0, y = 0; if (transform) { rt->SetTransform(D2D1::Matrix3x2F(transform->m11, transform->m12, @@ -1553,15 +1738,6 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) return CAIRO_INT_STATUS_UNSUPPORTED; - /* If we have a fallback mask clip set on the dst, we have - * to go through the fallback path */ - if (!_cairo_surface_is_win32_printing (&dst->base)) { - if (clip != NULL) - _cairo_win32_display_surface_set_clip (to_win32_display_surface (dst), clip); - else - _cairo_win32_display_surface_unset_clip (to_win32_display_surface (dst)); - } - /* It is vital that dx values for dxy_buf are calculated from the delta of * _logical_ x coordinates (not user x coordinates) or else the sum of all * previous dx values may start to diverge from the current glyph's x @@ -1576,96 +1752,7 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run.glyphOffsets); BOOL transform = FALSE; - /* Needed to calculate bounding box for efficient blitting */ - INT32 smallestX = INT_MAX; - INT32 largestX = 0; - INT32 smallestY = INT_MAX; - INT32 largestY = 0; - for (int i = 0; i < num_glyphs; i++) { - if (glyphs[i].x < smallestX) { - smallestX = (INT32)glyphs[i].x; - } - if (glyphs[i].x > largestX) { - largestX = (INT32)glyphs[i].x; - } - if (glyphs[i].y < smallestY) { - smallestY = (INT32)glyphs[i].y; - } - if (glyphs[i].y > largestY) { - largestY = (INT32)glyphs[i].y; - } - } - /** - * Here we try to get a rough estimate of the area that this glyph run will - * cover on the surface. Since we use GDI interop to draw we will be copying - * data around the size of the area of the surface that we map. We will want - * to map an area as small as possible to prevent large surfaces to be - * copied around. We take the X/Y-size of the font as margin on the left/top - * twice the X/Y-size of the font as margin on the right/bottom. - * This should always cover the entire area where the glyphs are. - */ - RECT fontArea; - fontArea.left = (INT32)(smallestX - scaled_font->font_matrix.xx); - fontArea.right = (INT32)(largestX + scaled_font->font_matrix.xx * 2); - fontArea.top = (INT32)(smallestY - scaled_font->font_matrix.yy); - fontArea.bottom = (INT32)(largestY + scaled_font->font_matrix.yy * 2); - if (fontArea.left < 0) - fontArea.left = 0; - if (fontArea.top < 0) - fontArea.top = 0; - if (fontArea.bottom > dst->extents.height) { - fontArea.bottom = dst->extents.height; - } - if (fontArea.right > dst->extents.width) { - fontArea.right = dst->extents.width; - } - if (fontArea.right <= fontArea.left || - fontArea.bottom <= fontArea.top) { - return CAIRO_INT_STATUS_SUCCESS; - } - if (fontArea.right > dst->extents.width) { - fontArea.right = dst->extents.width; - } - if (fontArea.bottom > dst->extents.height) { - fontArea.bottom = dst->extents.height; - } - - run.bidiLevel = 0; - run.fontFace = dwriteff->dwriteface; - run.isSideways = FALSE; - if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && - dwritesf->mat.xx == scaled_font->font_matrix.xx && - dwritesf->mat.yy == scaled_font->font_matrix.yy) { - - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - // Since we will multiply by our ctm matrix later for rotation effects - // and such, adjust positions by the inverse matrix now. - offsets[i].ascenderOffset = (FLOAT)(fontArea.top - glyphs[i].y); - offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left); - advances[i] = 0.0; - } - run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy; - } else { - transform = TRUE; - // See comment about EPSILON in _cairo_dwrite_glyph_run_from_glyphs - const double EPSILON = 0.0001; - for (int i = 0; i < num_glyphs; i++) { - indices[i] = (WORD) glyphs[i].index; - double x = glyphs[i].x - fontArea.left + EPSILON; - double y = glyphs[i].y - fontArea.top; - cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); - /** - * Since we will multiply by our ctm matrix later for rotation effects - * and such, adjust positions by the inverse matrix now. The Y-axis - * is inverted so the offset becomes negative. - */ - offsets[i].ascenderOffset = -(FLOAT)y; - offsets[i].advanceOffset = (FLOAT)x; - advances[i] = 0.0; - } - run.fontEmSize = 1.0f; - } + _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, dwritesf, &run, &transform); cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; COLORREF color = RGB(((int)solid_pattern->color.red_short) >> 8, @@ -1681,18 +1768,31 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, mat = NULL; } - RECT area; - area.left = dst->extents.x; - area.top = dst->extents.y; - area.right = area.left + dst->extents.width; - area.bottom = area.top + dst->extents.height; + RefPtr<IDWriteGlyphRunAnalysis> runAnalysis; + HRESULT hr = DWriteFactory::Instance()-> + CreateGlyphRunAnalysis(&run, 1, mat, + DWRITE_RENDERING_MODE_ALIASED, + dwritesf->measuring_mode, + 0, // baselineOriginX, + 0, // baselineOriginY, + &runAnalysis); + if (FAILED(hr)) + return CAIRO_INT_STATUS_UNSUPPORTED; + RECT fontArea; + hr = runAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1, &fontArea); + if (FAILED(hr)) + return CAIRO_INT_STATUS_UNSUPPORTED; + InflateRect(&fontArea, 1, 1); + /* Needed to calculate bounding box for efficient blitting */ + RECT copyArea, dstArea = { 0, 0, dst->extents.width, dst->extents.height }; + IntersectRect(©Area, &fontArea, &dstArea); #ifdef CAIRO_TRY_D2D_TO_GDI status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst, mat, &run, color, - fontArea); + copyArea); if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) { #endif @@ -1701,7 +1801,7 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, &run, color, dwritesf, - fontArea); + copyArea); #ifdef CAIRO_TRY_D2D_TO_GDI } @@ -1710,59 +1810,6 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, return status; } -#define ENHANCED_CONTRAST_REGISTRY_KEY \ - HKEY_CURRENT_USER, "Software\\Microsoft\\Avalon.Graphics\\DISPLAY1\\EnhancedContrastLevel" - -void -DWriteFactory::CreateRenderingParams() -{ - if (!Instance()) { - return; - } - - Instance()->CreateRenderingParams(&mDefaultRenderingParams); - - // For EnhancedContrast, we override the default if the user has not set it - // in the registry (by using the ClearType Tuner). - FLOAT contrast; - if (mEnhancedContrast >= 0.0 && mEnhancedContrast <= 10.0) { - contrast = mEnhancedContrast; - } else { - HKEY hKey; - if (RegOpenKeyExA(ENHANCED_CONTRAST_REGISTRY_KEY, - 0, KEY_READ, &hKey) == ERROR_SUCCESS) - { - contrast = mDefaultRenderingParams->GetEnhancedContrast(); - RegCloseKey(hKey); - } else { - contrast = 1.0; - } - } - - // For parameters that have not been explicitly set via the SetRenderingParams API, - // we copy values from default params (or our overridden value for contrast) - FLOAT gamma = - mGamma >= 1.0 && mGamma <= 2.2 ? - mGamma : mDefaultRenderingParams->GetGamma(); - FLOAT clearTypeLevel = - mClearTypeLevel >= 0.0 && mClearTypeLevel <= 1.0 ? - mClearTypeLevel : mDefaultRenderingParams->GetClearTypeLevel(); - DWRITE_PIXEL_GEOMETRY pixelGeometry = - mPixelGeometry >= DWRITE_PIXEL_GEOMETRY_FLAT && mPixelGeometry <= DWRITE_PIXEL_GEOMETRY_BGR ? - (DWRITE_PIXEL_GEOMETRY)mPixelGeometry : mDefaultRenderingParams->GetPixelGeometry(); - DWRITE_RENDERING_MODE renderingMode = - mRenderingMode >= DWRITE_RENDERING_MODE_DEFAULT && mRenderingMode <= DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC ? - (DWRITE_RENDERING_MODE)mRenderingMode : mDefaultRenderingParams->GetRenderingMode(); - - Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, - pixelGeometry, renderingMode, - &mCustomClearTypeRenderingParams); - - Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, - pixelGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, - &mForceGDIClassicRenderingParams); -} - /* Check if a specific font table in a DWrite font and a scaled font is identical */ static cairo_int_status_t compare_font_tables (cairo_dwrite_font_face_t *dwface, @@ -1866,6 +1913,16 @@ _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_ } cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font); + if (cairo_font_face_status (face) == CAIRO_STATUS_SUCCESS && + cairo_font_face_get_type (face) == CAIRO_FONT_TYPE_TOY) + { + face = ((cairo_toy_font_face_t *)face)->impl_face; + } + + if (face == NULL || cairo_font_face_get_type (face) != CAIRO_FONT_TYPE_DWRITE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t*>(face); RefPtr<IDWriteGdiInterop> gdiInterop; @@ -1897,6 +1954,7 @@ _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_ cairo_scaled_font_get_ctm (scaled_font, &ctm); cairo_font_options_t options; + _cairo_font_options_init_default (&options); cairo_scaled_font_get_font_options (scaled_font, &options); cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face, diff --git a/src/win32/cairo-dwrite-private.hpp b/src/win32/cairo-dwrite-private.hpp index 92b096857..c7a24822a 100644 --- a/src/win32/cairo-dwrite-private.hpp +++ b/src/win32/cairo-dwrite-private.hpp @@ -36,27 +36,25 @@ #include "cairoint.h" #include "cairo-win32-refptr.hpp" -#include <dwrite.h> -#include <dwrite_2.h> +#include <dwrite_3.h> #include <d2d1.h> -/* If either of the dwrite_3.h or d2d1_3.h headers required for color fonts - * are not available, include our own version containing just the functions we need. - */ - -#if HAVE_DWRITE_3_H -#include <dwrite_3.h> -#else +#ifdef __MINGW32__ #include "dw-extra.h" +#else +typedef DWRITE_COLOR_GLYPH_RUN1 DWRITE_COLOR_GLYPH_RUN1_WORKAROUND; #endif +/* If d2d1_3.h header required for color fonts is not available, + * include our own version containing just the functions we need. + */ + #if HAVE_D2D1_3_H #include <d2d1_3.h> #else #include "d2d1-extra.h" #endif - // DirectWrite is not available on all platforms. typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)( DWRITE_FACTORY_TYPE factoryType, @@ -70,14 +68,8 @@ struct _cairo_dwrite_scaled_font { cairo_matrix_t mat; cairo_matrix_t mat_inverse; cairo_antialias_t antialias_mode; + IDWriteRenderingParams *rendering_params; DWRITE_MEASURING_MODE measuring_mode; - enum TextRenderingState { - TEXT_RENDERING_UNINITIALIZED, - TEXT_RENDERING_NO_CLEARTYPE, - TEXT_RENDERING_NORMAL, - TEXT_RENDERING_GDI_CLASSIC - }; - TextRenderingState rendering_mode; }; typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t; @@ -107,6 +99,36 @@ public: return mFactoryInstance; } + static RefPtr<IDWriteFactory1> Instance1() + { + if (!mFactoryInstance1) { + if (Instance()) { + Instance()->QueryInterface(&mFactoryInstance1); + } + } + return mFactoryInstance1; + } + + static RefPtr<IDWriteFactory2> Instance2() + { + if (!mFactoryInstance2) { + if (Instance()) { + Instance()->QueryInterface(&mFactoryInstance2); + } + } + return mFactoryInstance2; + } + + static RefPtr<IDWriteFactory3> Instance3() + { + if (!mFactoryInstance3) { + if (Instance()) { + Instance()->QueryInterface(&mFactoryInstance3); + } + } + return mFactoryInstance3; + } + static RefPtr<IDWriteFactory4> Instance4() { if (!mFactoryInstance4) { @@ -145,69 +167,24 @@ public: return family; } - static RefPtr<IDWriteRenderingParams> RenderingParams(cairo_dwrite_scaled_font_t::TextRenderingState mode) - { - if (!mDefaultRenderingParams || - !mForceGDIClassicRenderingParams || - !mCustomClearTypeRenderingParams) - { - CreateRenderingParams(); - } - RefPtr<IDWriteRenderingParams> params; - if (mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE) { - params = mDefaultRenderingParams; - } else if (mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC && mRenderingMode < 0) { - params = mForceGDIClassicRenderingParams; - } else { - params = mCustomClearTypeRenderingParams; - } - return params; - } - - static void SetRenderingParams(FLOAT aGamma, - FLOAT aEnhancedContrast, - FLOAT aClearTypeLevel, - int aPixelGeometry, - int aRenderingMode) + static RefPtr<IDWriteRenderingParams> DefaultRenderingParams() { - mGamma = aGamma; - mEnhancedContrast = aEnhancedContrast; - mClearTypeLevel = aClearTypeLevel; - mPixelGeometry = aPixelGeometry; - mRenderingMode = aRenderingMode; - // discard any current RenderingParams objects - if (mCustomClearTypeRenderingParams) { - mCustomClearTypeRenderingParams->Release(); - mCustomClearTypeRenderingParams = NULL; - } - if (mForceGDIClassicRenderingParams) { - mForceGDIClassicRenderingParams->Release(); - mForceGDIClassicRenderingParams = NULL; - } - if (mDefaultRenderingParams) { - mDefaultRenderingParams->Release(); - mDefaultRenderingParams = NULL; + if (!mDefaultRenderingParams) { + if (Instance()) { + Instance()->CreateRenderingParams(&mDefaultRenderingParams); + } } - } - - static int GetClearTypeRenderingMode() { - return mRenderingMode; + return mDefaultRenderingParams; } private: - static void CreateRenderingParams(); - static RefPtr<IDWriteFactory> mFactoryInstance; + static RefPtr<IDWriteFactory1> mFactoryInstance1; + static RefPtr<IDWriteFactory2> mFactoryInstance2; + static RefPtr<IDWriteFactory3> mFactoryInstance3; static RefPtr<IDWriteFactory4> mFactoryInstance4; static RefPtr<IDWriteFontCollection> mSystemCollection; static RefPtr<IDWriteRenderingParams> mDefaultRenderingParams; - static RefPtr<IDWriteRenderingParams> mCustomClearTypeRenderingParams; - static RefPtr<IDWriteRenderingParams> mForceGDIClassicRenderingParams; - static FLOAT mGamma; - static FLOAT mEnhancedContrast; - static FLOAT mClearTypeLevel; - static int mPixelGeometry; - static int mRenderingMode; }; class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN @@ -251,5 +228,7 @@ struct _cairo_dwrite_font_face { cairo_font_face_t base; IDWriteFontFace *dwriteface; /* Can't use RefPtr because this struct is malloc'd. */ cairo_bool_t have_color; + IDWriteRenderingParams *rendering_params; /* Can't use RefPtr because this struct is malloc'd. */ + DWRITE_MEASURING_MODE measuring_mode; }; typedef struct _cairo_dwrite_font_face cairo_dwrite_font_face_t; diff --git a/src/win32/cairo-win32-debug.c b/src/win32/cairo-win32-debug.c index 5a971bd61..01410c896 100644 --- a/src/win32/cairo-win32-debug.c +++ b/src/win32/cairo-win32-debug.c @@ -37,15 +37,6 @@ * Vladimir Vukicevic <vladimir@pobox.com> */ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include "cairoint.h" #include "cairo-win32-private.h" diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c index 6fce722ec..781ee0cde 100644 --- a/src/win32/cairo-win32-device.c +++ b/src/win32/cairo-win32-device.c @@ -37,15 +37,6 @@ * Vladimir Vukicevic <vladimir@pobox.com> */ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include "cairoint.h" #include "cairo-atomic-private.h" diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c index 841725474..e3b3eec2f 100644 --- a/src/win32/cairo-win32-display-surface.c +++ b/src/win32/cairo-win32-display-surface.c @@ -37,15 +37,6 @@ * Vladimir Vukicevic <vladimir@pobox.com> */ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include "cairoint.h" #include "cairo-clip-private.h" diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c index 2003a2782..a561e74a4 100644 --- a/src/win32/cairo-win32-font.c +++ b/src/win32/cairo-win32-font.c @@ -33,15 +33,6 @@ * Contributor(s): */ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as GetGlyphIndices */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include "cairoint.h" #include "cairo-win32-private.h" @@ -80,7 +71,6 @@ * * Note: Win32 GDI fonts do not support color fonts. Use DWrite fonts * if color font support is required. - **/ /** @@ -1345,9 +1335,9 @@ _cairo_win32_scaled_font_load_type1_data (void *abstract_font, length); } -static cairo_surface_t * -_compute_mask (cairo_surface_t *surface, - int quality) +cairo_surface_t * +_cairo_compute_glyph_mask (cairo_surface_t *surface, + int quality) { cairo_image_surface_t *glyph; cairo_image_surface_t *mask; @@ -1431,7 +1421,7 @@ _cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_f if (status) goto FAIL; - image = _compute_mask (surface, scaled_font->quality); + image = _cairo_compute_glyph_mask (surface, scaled_font->quality); status = image->status; if (status) goto FAIL; @@ -1808,7 +1798,7 @@ _cairo_win32_font_face_scaled_font_create (void *abstract_face, if (font_face->hfont) { /* Check whether it's OK to go ahead and use the font-face's HFONT. */ if (_is_scale (ctm, 1.) && - _is_scale (font_matrix, -font_face->logfont.lfHeight)) { + _is_scale (font_matrix, -font_face->logfont.lfHeight * WIN32_FONT_LOGICAL_SCALE)) { hfont = font_face->hfont; } } diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c index 1d1d7f873..bc1f69e70 100644 --- a/src/win32/cairo-win32-gdi-compositor.c +++ b/src/win32/cairo-win32-gdi-compositor.c @@ -602,7 +602,8 @@ static cairo_bool_t check_glyphs (cairo_composite_rectangles_t *composite, if (! _cairo_clip_is_region (composite->clip)) return FALSE; - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) + cairo_font_type_t type = cairo_scaled_font_get_type (scaled_font); + if (type != CAIRO_FONT_TYPE_WIN32 && type != CAIRO_FONT_TYPE_DWRITE) return FALSE; if (! _cairo_pattern_is_opaque_solid (&composite->source_pattern.base)) diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c index 602a4f3f0..a3dd907c2 100644 --- a/src/win32/cairo-win32-printing-surface.c +++ b/src/win32/cairo-win32-printing-surface.c @@ -35,15 +35,6 @@ * Vladimir Vukicevic <vladimir@pobox.com> */ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include "cairoint.h" #include "cairo-default-context-private.h" @@ -663,6 +654,7 @@ _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_printing_surf SaveDC (surface->win32.dc); /* Allow clip path to be reset during replay */ status = _cairo_recording_surface_replay_region (&recording_surface->base, + pattern->region_array_id, is_subsurface ? &recording_extents : NULL, &surface->win32.base, CAIRO_RECORDING_REGION_NATIVE); @@ -2162,6 +2154,9 @@ _cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_su * provide correct complex rendering behaviour; cairo_surface_show_page() and * associated methods must be used for correct output. * + * The following mime types are supported on source patterns: + * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG. + * * Return value: the newly created surface * * Since: 1.6 diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index d457b7805..6af09c0e1 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -214,6 +214,10 @@ cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); +cairo_surface_t * +_cairo_compute_glyph_mask (cairo_surface_t *surface, + int quality); + uint32_t _cairo_win32_flags_for_dc (HDC dc, cairo_format_t format); @@ -271,9 +275,6 @@ cairo_int_status_t _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, cairo_scaled_font_t **new_font); -cairo_font_face_t* -cairo_dwrite_font_face_create_for_dwrite_fontface_internal(void* dwrite_font_face); - #endif /* CAIRO_HAS_DWRITE_FONT */ CAIRO_END_DECLS diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index e53c16540..ca5c9d823 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -37,15 +37,6 @@ * Vladimir Vukicevic <vladimir@pobox.com> */ -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include "cairoint.h" #include "cairo-backend-private.h" diff --git a/src/win32/cairo-win32-system.c b/src/win32/cairo-win32-system.c index 878553009..01bbe89df 100644 --- a/src/win32/cairo-win32-system.c +++ b/src/win32/cairo-win32-system.c @@ -50,15 +50,6 @@ #if CAIRO_MUTEX_IMPL_WIN32 #if !CAIRO_WIN32_STATIC_BUILD -#define WIN32_LEAN_AND_MEAN -/* We require Windows 2000 features such as ETO_PDY */ -#if !defined(WINVER) || (WINVER < 0x0500) -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# define _WIN32_WINNT 0x0500 -#endif - #include <windows.h> /* declare to avoid "no previous prototype for 'DllMain'" warning */ diff --git a/src/win32/dw-extra.h b/src/win32/dw-extra.h index 4203f3427..c79e6389c 100644 --- a/src/win32/dw-extra.h +++ b/src/win32/dw-extra.h @@ -1,165 +1,30 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* Mingw-w64 dwrite_3.h is broken +/* MinGW workarounds * - * We only need the definitions of the following functions and their dependencies. - * IDWriteFactory4::TranslateColorGlyphRun() - * IDWriteFontResource::GetDefaultFontAxisValues() - * IDWriteFontResource::GetFontAxisCount() - * IDWriteFontResource::HasVariations() - * IDWriteFontFace5::GetFontAxisValueCount() - * IDWriteFontFace5::GetFontAxisValues() - * IDWriteFontFace5::HasVariations() - * IDWriteFontFace5::GetFontResource() - * - * But we need to include all the prior functions in the same struct, - * and parent structs, so that the functions are in the correct position - * in the vtable. The parameters of the unused functions are not - * required as we only need a function in the struct to create a - * function pointer in the vtable. + * It doesn't define operators for DWRITE_GLYPH_IMAGE_FORMATS. + * The DWRITE_COLOR_GLYPH_RUN struct isn't valid. + * <https://sourceforge.net/p/mingw-w64/bugs/913/> */ #ifndef DWRITE_EXTRA_H #define DWRITE_EXTRA_H -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" - -#include <dwrite_2.h> +#if defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR < 10 -interface IDWriteFactory3; -interface IDWriteFactory4; -interface IDWriteColorGlyphRunEnumerator1; - -DEFINE_ENUM_FLAG_OPERATORS(DWRITE_GLYPH_IMAGE_FORMATS); - -struct DWRITE_COLOR_GLYPH_RUN1 : DWRITE_COLOR_GLYPH_RUN +typedef struct DWRITE_COLOR_GLYPH_RUN1_WORKAROUND DWRITE_COLOR_GLYPH_RUN1_WORKAROUND; +struct DWRITE_COLOR_GLYPH_RUN1_WORKAROUND : DWRITE_COLOR_GLYPH_RUN { DWRITE_GLYPH_IMAGE_FORMATS glyphImageFormat; DWRITE_MEASURING_MODE measuringMode; }; +#else +typedef DWRITE_COLOR_GLYPH_RUN1 DWRITE_COLOR_GLYPH_RUN1_WORKAROUND; +#error -DEFINE_GUID(IID_IDWriteColorGlyphRunEnumerator1, 0x7c5f86da, 0xc7a1, 0x4f05, 0xb8,0xe1, 0x55,0xa1,0x79,0xfe,0x5a,0x35); -MIDL_INTERFACE("7c5f86da-c7a1-4f05-b8e1-55a179fe5a35") -IDWriteColorGlyphRunEnumerator1 : public IDWriteColorGlyphRunEnumerator -{ - virtual HRESULT STDMETHODCALLTYPE GetCurrentRun( - const DWRITE_COLOR_GLYPH_RUN1 **run) = 0; - -}; -__CRT_UUID_DECL(IDWriteColorGlyphRunEnumerator1, 0x7c5f86da, 0xc7a1, 0x4f05, 0xb8,0xe1, 0x55,0xa1,0x79,0xfe,0x5a,0x35) - -DEFINE_GUID(IID_IDWriteFactory3, 0x9a1b41c3, 0xd3bb, 0x466a, 0x87,0xfc, 0xfe,0x67,0x55,0x6a,0x3b,0x65); -MIDL_INTERFACE("9a1b41c3-d3bb-466a-87fc-fe67556a3b65") -IDWriteFactory3 : public IDWriteFactory2 -{ - virtual void STDMETHODCALLTYPE CreateGlyphRunAnalysis() = 0; - virtual void STDMETHODCALLTYPE CreateCustomRenderingParams() = 0; - virtual void STDMETHODCALLTYPE CreateFontFaceReference() = 0; - virtual void STDMETHODCALLTYPE CreateFontFaceReference2() = 0; - virtual void STDMETHODCALLTYPE GetSystemFontSet() = 0; - virtual void STDMETHODCALLTYPE CreateFontSetBuilder() = 0; - virtual void STDMETHODCALLTYPE CreateFontCollectionFromFontSet() = 0; - virtual void STDMETHODCALLTYPE GetSystemFontCollection() = 0; - virtual void STDMETHODCALLTYPE GetFontDownloadQueue() = 0; -}; -__CRT_UUID_DECL(IDWriteFactory3, 0x9a1b41c3, 0xd3bb, 0x466a, 0x87,0xfc, 0xfe,0x67,0x55,0x6a,0x3b,0x65) - -DEFINE_GUID(IID_IDWriteFactory4, 0x4b0b5bd3, 0x0797, 0x4549, 0x8a,0xc5, 0xfe,0x91,0x5c,0xc5,0x38,0x56); -MIDL_INTERFACE("4b0b5bd3-0797-4549-8ac5-fe915cc53856") -IDWriteFactory4 : public IDWriteFactory3 -{ - virtual HRESULT STDMETHODCALLTYPE TranslateColorGlyphRun( - D2D1_POINT_2F baselineOrigin, - DWRITE_GLYPH_RUN const *glyphRun, - DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription, - DWRITE_GLYPH_IMAGE_FORMATS desiredGlyphImageFormats, - DWRITE_MEASURING_MODE measuringMode, - DWRITE_MATRIX const *worldAndDpiTransform, - UINT32 colorPaletteIndex, - IDWriteColorGlyphRunEnumerator1 **colorLayers) = 0; -}; -__CRT_UUID_DECL(IDWriteFactory4, 0x4b0b5bd3, 0x0797, 0x4549, 0x8a,0xc5, 0xfe,0x91,0x5c,0xc5,0x38,0x56) - -typedef enum DWRITE_FONT_AXIS_TAG { - DWRITE_FONT_AXIS_TAG_WEIGHT = 0x74686777, - DWRITE_FONT_AXIS_TAG_WIDTH = 0x68746477, - DWRITE_FONT_AXIS_TAG_SLANT = 0x746e6c73, - DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE = 0x7a73706f, - DWRITE_FONT_AXIS_TAG_ITALIC = 0x6c617469 -} DWRITE_FONT_AXIS_TAG; - -typedef struct DWRITE_FONT_AXIS_VALUE { - DWRITE_FONT_AXIS_TAG axisTag; - FLOAT value; -} DWRITE_FONT_AXIS_VALUE; - -DEFINE_GUID(IID_IDWriteFontResource, 0x1f803a76, 0x6871, 0x48e8, 0x98,0x7f, 0xb9,0x75,0x55,0x1c,0x50,0xf2); -MIDL_INTERFACE("1f803a76-6871-48e8-987f-b975551c50f2") -IDWriteFontResource : public IUnknown -{ - virtual void STDMETHODCALLTYPE GetFontFile() = 0; - virtual void STDMETHODCALLTYPE GetFontFaceIndex() = 0; - virtual UINT32 STDMETHODCALLTYPE GetFontAxisCount() = 0; - virtual HRESULT STDMETHODCALLTYPE GetDefaultFontAxisValues( - const DWRITE_FONT_AXIS_VALUE *values, - UINT32 num_values) = 0; - virtual void STDMETHODCALLTYPE GetFontAxisRanges() = 0; - virtual void STDMETHODCALLTYPE GetFontAxisAttributes() = 0; - virtual void STDMETHODCALLTYPE GetAxisNames() = 0; - virtual void STDMETHODCALLTYPE GetAxisValueNameCount() = 0; - virtual void STDMETHODCALLTYPE GetAxisValueNames() = 0; - virtual WINBOOL STDMETHODCALLTYPE HasVariations() = 0; - virtual void STDMETHODCALLTYPE CreateFontFace() = 0; - virtual void STDMETHODCALLTYPE CreateFontFaceReference() = 0; -}; -__CRT_UUID_DECL(IDWriteFontResource, 0x1f803a76, 0x6871, 0x48e8, 0x98,0x7f, 0xb9,0x75,0x55,0x1c,0x50,0xf2) - -DEFINE_GUID(IID_IDWriteFontFace3, 0xd37d7598, 0x09be, 0x4222, 0xa2,0x36, 0x20,0x81,0x34,0x1c,0xc1,0xf2); -MIDL_INTERFACE("d37d7598-09be-4222-a236-2081341cc1f2") -IDWriteFontFace3 : public IDWriteFontFace2 -{ - virtual void STDMETHODCALLTYPE GetFontFaceReference() = 0; - virtual void STDMETHODCALLTYPE GetPanose() = 0; - virtual void STDMETHODCALLTYPE GetWeight() = 0; - virtual void STDMETHODCALLTYPE GetStretch() = 0; - virtual void STDMETHODCALLTYPE GetStyle() = 0; - virtual void STDMETHODCALLTYPE GetFamilyNames() = 0; - virtual void STDMETHODCALLTYPE GetFaceNames() = 0; - virtual void STDMETHODCALLTYPE GetInformationalStrings() = 0; - virtual void STDMETHODCALLTYPE HasCharacter() = 0; - virtual void STDMETHODCALLTYPE GetRecommendedRenderingMode() = 0; - virtual void STDMETHODCALLTYPE IsCharacterLocal() = 0; - virtual void STDMETHODCALLTYPE IsGlyphLocal() = 0; - virtual void STDMETHODCALLTYPE AreCharactersLocal() = 0; - virtual void STDMETHODCALLTYPE AreGlyphsLocal() = 0; -}; -__CRT_UUID_DECL(IDWriteFontFace3, 0xd37d7598, 0x09be, 0x4222, 0xa2,0x36, 0x20,0x81,0x34,0x1c,0xc1,0xf2) - -DEFINE_GUID(IID_IDWriteFontFace4, 0x27f2a904, 0x4eb8, 0x441d, 0x96,0x78, 0x05,0x63,0xf5,0x3e,0x3e,0x2f); -MIDL_INTERFACE("27f2a904-4eb8-441d-9678-0563f53e3e2f") -IDWriteFontFace4 : public IDWriteFontFace3 -{ - virtual void STDMETHODCALLTYPE GetGlyphImageFormats_() = 0; - virtual void STDMETHODCALLTYPE GetGlyphImageFormats() = 0; - virtual void STDMETHODCALLTYPE GetGlyphImageData() = 0; - virtual void STDMETHODCALLTYPE ReleaseGlyphImageData() = 0; -}; -__CRT_UUID_DECL(IDWriteFontFace4, 0x27f2a904, 0x4eb8, 0x441d, 0x96,0x78, 0x05,0x63,0xf5,0x3e,0x3e,0x2f) +#endif -DEFINE_GUID(IID_IDWriteFontFace5, 0x98eff3a5, 0xb667, 0x479a, 0xb1,0x45, 0xe2,0xfa,0x5b,0x9f,0xdc,0x29); -MIDL_INTERFACE("98eff3a5-b667-479a-b145-e2fa5b9fdc29") -IDWriteFontFace5 : public IDWriteFontFace4 -{ - virtual UINT32 STDMETHODCALLTYPE GetFontAxisValueCount() = 0; - virtual HRESULT STDMETHODCALLTYPE GetFontAxisValues( - DWRITE_FONT_AXIS_VALUE *values, - UINT32 value_count) = 0; - virtual WINBOOL STDMETHODCALLTYPE HasVariations() = 0; - virtual HRESULT STDMETHODCALLTYPE GetFontResource( - IDWriteFontResource **resource) = 0; - virtual void STDMETHODCALLTYPE Equals() = 0; -}; -__CRT_UUID_DECL(IDWriteFontFace5, 0x98eff3a5, 0xb667, 0x479a, 0xb1,0x45, 0xe2,0xfa,0x5b,0x9f,0xdc,0x29) +DEFINE_ENUM_FLAG_OPERATORS(DWRITE_GLYPH_IMAGE_FORMATS); #endif /* DWRITE_EXTRA_H */ |